redis_cluster 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +118 -40
- data/lib/redis_cluster/client.rb +166 -79
- data/lib/redis_cluster/node.rb +3 -1
- data/lib/redis_cluster/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e10ed9adf21d43959b884dd43b59474b469c84cf
|
4
|
+
data.tar.gz: dfab801d69bf40c1d92fdb3f123ba980cc7aeade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0e96edb02ff52f9a4eda36d1e922288a4459669d645ac736a8856a35a4cdbc2ca10b0cc37ab6560b4302eabb2270fe1ead8d9fa4cfb4bea53360ffaab8cae8e
|
7
|
+
data.tar.gz: 80706f7728f362931760c1473bf7f01b68ee7e95953d85da47126170c43d30af0e138e5116179f8e11fd54544e451f8714351a5148b6c17cc071e4a70a42b741
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,79 +2,158 @@
|
|
2
2
|
|
3
3
|
![travis ci](https://travis-ci.org/zhchsf/redis_cluster.svg?branch=master)
|
4
4
|
|
5
|
-
|
5
|
+
Support: Ruby 2.0+
|
6
6
|
|
7
|
-
|
7
|
+
Redis Cluster is a Redis configuration that allows data to be automatically
|
8
|
+
sharded across a number of different nodes. You can find its main documentation
|
9
|
+
at https://redis.io/topics/cluster-tutorial.
|
8
10
|
|
11
|
+
[`redis-rb`](https://github.com/redis/redis-rb), the most common Redis gem for
|
12
|
+
Ruby, doesn't offer Redis Cluster support. This gem works in conjunction with
|
13
|
+
redis-rb to add the missing functionality. It's based on [antirez's prototype
|
14
|
+
reference implementation](https://github.com/antirez/redis-rb-cluster) (which
|
15
|
+
is not maintained).
|
9
16
|
|
10
17
|
## Installation
|
11
18
|
|
12
|
-
Add
|
19
|
+
Add it to your `Gemfile`:
|
13
20
|
|
14
21
|
```ruby
|
15
22
|
gem 'redis_cluster'
|
16
23
|
```
|
17
24
|
|
18
|
-
|
25
|
+
## Usage
|
19
26
|
|
20
|
-
|
27
|
+
Initialize `RedisCluster` with an array of Redis Cluster host nodes:
|
21
28
|
|
22
|
-
|
29
|
+
```ruby
|
30
|
+
rs = RedisCluster.new([
|
31
|
+
{host: '127.0.0.1', port: 7000},
|
32
|
+
{host: '127.0.0.1', port: 7001}
|
33
|
+
])
|
34
|
+
rs.set "test", 1
|
35
|
+
rs.get "test"
|
36
|
+
```
|
23
37
|
|
24
|
-
|
38
|
+
The library will issue the `CLUSTER SLOTS` command to configured hosts it it
|
39
|
+
receives a `MOVED` response, so it's safe to configure it with only a subset of
|
40
|
+
the total nodes in the cluster.
|
25
41
|
|
26
|
-
|
42
|
+
### Other options
|
27
43
|
|
28
|
-
|
44
|
+
Most options are forwarded onto underlying `Redis` clients. If for example
|
45
|
+
`masterauth` and `requirepass` are enabled, the password can be set like this:
|
29
46
|
|
30
47
|
```ruby
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
48
|
+
RedisCluster.new(hosts, password: 'password')
|
49
|
+
```
|
50
|
+
|
51
|
+
### Standalone Redis
|
52
|
+
|
53
|
+
If initialized with a host hash instead of an array, the library will assume
|
54
|
+
that it's operating on a standalone Redis, and cluster functionality will be
|
55
|
+
disabled:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
rs = RedisCluster.new({host: '127.0.0.1', port: 7000})
|
36
59
|
```
|
37
60
|
|
38
|
-
|
61
|
+
When configured with an array of hosts the library normally requires that they
|
62
|
+
be part of a Redis Cluster, but that check can be disabled by setting
|
63
|
+
`force_cluster: false`. This may be useful for development or test environments
|
64
|
+
where a full cluster isn't available, but where a standalone Redis will do just
|
65
|
+
as well.
|
39
66
|
|
40
|
-
If masterauth & requirepass configed, you can initialize below:
|
41
67
|
```ruby
|
42
|
-
RedisCluster.new
|
68
|
+
rs = RedisCluster.new([
|
69
|
+
{host: '127.0.0.1', port: 7000},
|
70
|
+
], force_cluster: false)
|
43
71
|
```
|
44
72
|
|
45
|
-
|
73
|
+
### Logging
|
74
|
+
|
75
|
+
A logger can be specified with the `logger` option. It should be compatible
|
76
|
+
with the interface of Ruby's `Logger` from the standard library.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'logger'
|
80
|
+
logger = Logger.new(STDOUT)
|
81
|
+
logger.level = Logger::WARN
|
82
|
+
RedisCluster.new(hosts, logger: logger)
|
83
|
+
```
|
84
|
+
|
85
|
+
### `KEYS`
|
86
|
+
|
87
|
+
The `KEYS` command will scan all nodes:
|
88
|
+
|
46
89
|
```ruby
|
47
90
|
rs.keys 'test*'
|
48
91
|
```
|
49
92
|
|
50
|
-
|
93
|
+
### Pipelining, `MULTI`
|
94
|
+
|
95
|
+
There is limited support for pipelining and `MULTI`:
|
96
|
+
|
51
97
|
```ruby
|
52
|
-
# Only support pipeline commands to one redis node once
|
53
|
-
# You must ensure keys at one slot: use same key or hash tags
|
54
|
-
# If you don't, not raise any errors now
|
55
98
|
rs.pipelined do
|
56
99
|
rs.set "{foo}one", 1
|
57
100
|
rs.set "{foo}two", 2
|
58
101
|
end
|
59
102
|
```
|
60
103
|
|
61
|
-
|
104
|
+
Note that all keys used in a pipeline must map to the same Redis node. This is
|
105
|
+
possible through the use of Redis Cluster "hash tags" where only the section of
|
106
|
+
a key name wrapped in `{}` when calculating a key's hash.
|
107
|
+
|
108
|
+
#### `EVAL`, `EVALSHA`, `SCRIPT`
|
109
|
+
|
110
|
+
`EVAL` and `EVALSHA` must only rely on keys that map to a single slot (again,
|
111
|
+
possible with hash tags). `KEYS` should be used to retrieve keys in Lua
|
112
|
+
scripts.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
rs.eval "return redis.call('get', KEYS[1]) + ARGV[1]", [:test], [3]
|
116
|
+
rs.evalsha '727fc2fb7c0f11ec134d998654e3dadaacf31a97', [:test], [5]
|
117
|
+
|
118
|
+
# Even if a Lua script doesn't need any keys or argvs, you'll still need to
|
119
|
+
specify a dummy key.
|
120
|
+
rs.eval "return 'hello redis!'", [:foo]
|
121
|
+
```
|
122
|
+
|
123
|
+
`SCRIPT` commands will run on all nodes:
|
124
|
+
|
62
125
|
```ruby
|
63
126
|
# script commands will run on all nodes
|
64
127
|
rs.script :load, "return redis.call('get', KEYS[1])"
|
65
128
|
rs.script :exists, '4e6d8fc8bb01276962cce5371fa795a7763657ae'
|
66
129
|
rs.script :flush
|
130
|
+
```
|
67
131
|
|
68
|
-
|
69
|
-
# and must use KEYS to fetch keys in lua script
|
70
|
-
rs.eval "return redis.call('get', KEYS[1]) + ARGV[1]", [:test], [3]
|
71
|
-
rs.evalsha '727fc2fb7c0f11ec134d998654e3dadaacf31a97', [:test], [5]
|
132
|
+
## Development
|
72
133
|
|
73
|
-
|
74
|
-
|
134
|
+
Clone the repository and then install dependencies:
|
135
|
+
|
136
|
+
```sh
|
137
|
+
bin/setup
|
75
138
|
```
|
76
139
|
|
77
|
-
|
140
|
+
Run tests:
|
141
|
+
|
142
|
+
```sh
|
143
|
+
rake spec
|
144
|
+
```
|
145
|
+
|
146
|
+
`bin/console` will bring up an interactive prompt for other experimentation.
|
147
|
+
|
148
|
+
### Releases
|
149
|
+
|
150
|
+
To release a new version, update the version number in `version.rb` and run
|
151
|
+
`bundle exec rake release`. This will create a Git tag for the version, push
|
152
|
+
Git commits and tags to GitHub, and push the `.gem` file to Rubygems.
|
153
|
+
|
154
|
+
The gem can be installed locally with `bundle exec rake install`.
|
155
|
+
|
156
|
+
### Benchmark test
|
78
157
|
|
79
158
|
```ruby
|
80
159
|
Benchmark.bm do |x|
|
@@ -96,19 +175,18 @@ Benchmark.bm do |x|
|
|
96
175
|
end
|
97
176
|
```
|
98
177
|
|
99
|
-
|
100
|
-
## Development
|
101
|
-
|
102
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
103
|
-
|
104
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
105
|
-
|
106
178
|
## Contributing
|
107
179
|
|
108
|
-
Bug reports and pull requests are welcome on GitHub
|
109
|
-
|
180
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended
|
181
|
+
to be a safe, welcoming space for collaboration, and contributors are expected
|
182
|
+
to adhere to the [Contributor Covenant](http://contributor-covenant.org) code
|
183
|
+
of conduct.
|
110
184
|
|
111
185
|
## License
|
112
186
|
|
113
|
-
The gem is available as open source under the terms of the [MIT
|
187
|
+
The gem is available as open source under the terms of the [MIT
|
188
|
+
License](http://opensource.org/licenses/MIT).
|
114
189
|
|
190
|
+
<!--
|
191
|
+
# vim: set tw=79:
|
192
|
+
-->
|
data/lib/redis_cluster/client.rb
CHANGED
@@ -4,8 +4,9 @@ module RedisCluster
|
|
4
4
|
|
5
5
|
class Client
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(hosts, configs = {})
|
8
|
+
@hosts = hosts.dup
|
9
|
+
@initial_hosts = hosts.dup
|
9
10
|
|
10
11
|
# Extract configuration options relevant to Redis Cluster.
|
11
12
|
|
@@ -13,65 +14,98 @@ module RedisCluster
|
|
13
14
|
# the option existed
|
14
15
|
@force_cluster = configs.delete(:force_cluster) { |_key| true }
|
15
16
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
|
17
|
+
# An optional logger. Should respond like the standard Ruby `Logger`:
|
18
|
+
#
|
19
|
+
# http://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html
|
20
|
+
@logger = configs.delete(:logger) { |_key| nil }
|
21
|
+
|
22
|
+
# The number of times to retry when it detects a failure that looks like
|
23
|
+
# it might be intermittent.
|
24
|
+
#
|
25
|
+
# It might be worth setting this to `0` if you'd like full visibility
|
26
|
+
# around what kinds of errors are occurring. Possibly in conjunction with
|
27
|
+
# your own out-of-library retry loop and/or circuit breaker.
|
28
|
+
@retry_count = configs.delete(:retry_count) { |_key| 2 }
|
23
29
|
|
24
30
|
# Any leftover configuration goes through to the pool and onto individual
|
25
31
|
# Redis clients.
|
26
32
|
@pool = Pool.new(configs)
|
27
33
|
@mutex = Mutex.new
|
28
34
|
|
29
|
-
reload_pool_nodes
|
35
|
+
reload_pool_nodes
|
30
36
|
end
|
31
37
|
|
32
38
|
def execute(method, args, &block)
|
33
39
|
asking = false
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
#
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
retried = false
|
41
|
+
|
42
|
+
# Note that there are two levels of retry loops here.
|
43
|
+
#
|
44
|
+
# The first is for intermittent failures like "host unreachable" or
|
45
|
+
# timeouts. These are retried a number of times equal to @retry_count.
|
46
|
+
#
|
47
|
+
# The second is when it receives an `ASK` or `MOVED` error response from
|
48
|
+
# Redis. In this case the client will complete re-enter its execution
|
49
|
+
# loop and retry the command after any necessary prework (if `MOVED`, it
|
50
|
+
# will attempt to reload the node pool first). This will only ever be
|
51
|
+
# retried one time (see notes below). This loop uses Ruby's `retry`
|
52
|
+
# syntax for blocks, so keep an eye out for that in the code below.
|
53
|
+
#
|
54
|
+
# It's worth noting that if these conditions ever combine, you could see
|
55
|
+
# more network attempts than @retry_count. An initial execution attempt
|
56
|
+
# might fail intermittently a couple times before sending a `MOVED`. The
|
57
|
+
# client will then attempt to reload the node pool, an operation which is
|
58
|
+
# also retried for intermittent failures. It could then return to the
|
59
|
+
# main execution and fail another couple of times intermittently. This
|
60
|
+
# should be an extreme edge case, but it's worth considering if you're
|
61
|
+
# running at large scale.
|
62
|
+
begin
|
63
|
+
retry_intermittent_loop do |attempt|
|
49
64
|
# Getting an error while executing may be an indication that we've
|
50
65
|
# lost the node that we were talking to and in that case it makes
|
51
66
|
# sense to try a different node and maybe reload our node pool (if
|
52
67
|
# the new node issues a `MOVE`).
|
53
|
-
try_random_node =
|
68
|
+
try_random_node = attempt > 0
|
54
69
|
|
55
|
-
|
56
|
-
|
70
|
+
return @pool.execute(method, args, {asking: asking, random_node: try_random_node}, &block)
|
71
|
+
end
|
72
|
+
rescue Redis::CommandError => e
|
73
|
+
unless @logger.nil?
|
74
|
+
@logger.error("redis_cluster: Received error: #{e}")
|
75
|
+
end
|
76
|
+
|
77
|
+
# This is a special condition to protect against a misbehaving library
|
78
|
+
# or server. After we've gotten one ASK or MOVED and retried once,
|
79
|
+
# we'll never do so a second time. Receiving two of any operations in a
|
80
|
+
# row is probably indicative of a problem and we don't want to get
|
81
|
+
# stuck in an infinite retry loop.
|
82
|
+
raise if retried
|
83
|
+
retried = true
|
84
|
+
|
85
|
+
err_code = e.to_s.split.first
|
86
|
+
case err_code
|
87
|
+
when 'ASK'
|
88
|
+
unless @logger.nil?
|
89
|
+
@logger.info("redis_cluster: Received ASK; retrying operation (#{e})")
|
90
|
+
end
|
57
91
|
|
58
|
-
|
59
|
-
|
92
|
+
asking = true
|
93
|
+
retry
|
60
94
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# `MOVED` indicates a permanent redirect which means that our slot
|
65
|
-
# mappings are stale: reload them.
|
66
|
-
reload_pool_nodes(false)
|
95
|
+
when 'MOVED'
|
96
|
+
unless @logger.nil?
|
97
|
+
@logger.info("redis_cluster: Received MOVED; retrying operation (#{e})")
|
67
98
|
end
|
99
|
+
|
100
|
+
# `MOVED` indicates a permanent redirect which means that our slot
|
101
|
+
# mappings are stale: reload them then try what we were doing again
|
102
|
+
reload_pool_nodes
|
103
|
+
retry
|
104
|
+
|
105
|
+
else
|
106
|
+
raise
|
68
107
|
end
|
69
108
|
end
|
70
|
-
|
71
|
-
# If we ran out of retries (the maximum number may have been set to 0),
|
72
|
-
# surface any error that was thrown back to the caller. We'd otherwise
|
73
|
-
# suppress the error, which would return something quite unexpected.
|
74
|
-
raise last_error
|
75
109
|
end
|
76
110
|
|
77
111
|
Configuration.method_names.each do |method_name|
|
@@ -84,13 +118,31 @@ module RedisCluster
|
|
84
118
|
execute(method, args, &block)
|
85
119
|
end
|
86
120
|
|
121
|
+
# Closes all open connections and reloads the client pool.
|
122
|
+
#
|
123
|
+
# Normally host information from the last time the node pool was reloaded
|
124
|
+
# is used, but if the `use_initial_hosts` is set to `true`, then the client
|
125
|
+
# is completely refreshed and the hosts that were specified when creating
|
126
|
+
# it originally are set instead.
|
127
|
+
def reconnect(options = {})
|
128
|
+
use_initial_hosts = options.fetch(:use_initial_hosts, false)
|
129
|
+
|
130
|
+
@hosts = @initial_hosts.dup if use_initial_hosts
|
131
|
+
|
132
|
+
@mutex.synchronize do
|
133
|
+
@pool.nodes.each{|node| node.connection.close}
|
134
|
+
@pool.nodes.clear
|
135
|
+
reload_pool_nodes_unsync
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
87
139
|
private
|
88
140
|
|
89
141
|
# Adds only a single node to the client pool and sets it result for the
|
90
142
|
# entire space of slots. This is useful when running either a standalone
|
91
143
|
# Redis or a single-node Redis Cluster.
|
92
144
|
def create_single_node_pool
|
93
|
-
host = @
|
145
|
+
host = @hosts
|
94
146
|
if host.is_a?(Array)
|
95
147
|
if host.length > 1
|
96
148
|
raise ArgumentError, "Can only create single node pool for single host"
|
@@ -102,15 +154,22 @@ module RedisCluster
|
|
102
154
|
end
|
103
155
|
|
104
156
|
@pool.add_node!(host, [(0..Configuration::HASH_SLOTS)])
|
157
|
+
|
158
|
+
unless @logger.nil?
|
159
|
+
@logger.info("redis_cluster: Initialized single node pool: #{host}")
|
160
|
+
end
|
105
161
|
end
|
106
162
|
|
107
|
-
def create_multi_node_pool
|
108
|
-
unless @
|
163
|
+
def create_multi_node_pool
|
164
|
+
unless @hosts.is_a?(Array)
|
109
165
|
raise ArgumentError, "Can only create multi-node pool for multiple hosts"
|
110
166
|
end
|
111
167
|
|
112
|
-
|
113
|
-
|
168
|
+
begin
|
169
|
+
retry_intermittent_loop do |attempt|
|
170
|
+
# Try a random host from our seed pool.
|
171
|
+
options = @hosts.sample
|
172
|
+
|
114
173
|
redis = Node.redis(@pool.global_configs.merge(options))
|
115
174
|
slots_mapping = redis.cluster("slots").group_by{|x| x[2]}
|
116
175
|
@pool.delete_except!(slots_mapping.keys)
|
@@ -118,32 +177,27 @@ module RedisCluster
|
|
118
177
|
slots_ranges = infos.map {|x| x[0]..x[1] }
|
119
178
|
@pool.add_node!({host: host[0], port: host[1]}, slots_ranges)
|
120
179
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
# asked for force Redis Cluster, in which case we assume this is
|
127
|
-
# a configuration problem and maybe raise an error.
|
128
|
-
create_single_node_pool
|
129
|
-
return
|
130
|
-
elsif raise_error
|
131
|
-
raise e
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
raise e if e.message =~ /NOAUTH\ Authentication\ required/
|
180
|
+
end
|
181
|
+
rescue Redis::CommandError => e
|
182
|
+
unless @logger.nil?
|
183
|
+
@logger.error("redis_cluster: Received error: #{e}")
|
184
|
+
end
|
136
185
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
|
186
|
+
if e.message =~ /cluster\ support\ disabled$/ && !@force_cluster
|
187
|
+
# We're running outside of cluster-mode -- just create a single-node
|
188
|
+
# pool and move on. The exception is if we've been asked for force
|
189
|
+
# Redis Cluster, in which case we assume this is a configuration
|
190
|
+
# problem and maybe raise an error.
|
191
|
+
create_single_node_pool
|
192
|
+
return
|
142
193
|
end
|
143
194
|
|
144
|
-
|
145
|
-
|
146
|
-
|
195
|
+
raise
|
196
|
+
end
|
197
|
+
|
198
|
+
unless @logger.nil?
|
199
|
+
mappings = @pool.nodes.map{|node| "#{node.slots} -> #{node.options}"}
|
200
|
+
@logger.info("redis_cluster: Initialized multi-node pool: #{mappings}")
|
147
201
|
end
|
148
202
|
end
|
149
203
|
|
@@ -151,23 +205,56 @@ module RedisCluster
|
|
151
205
|
# SLOTS` or just adding a node directly if running on standalone. Clients
|
152
206
|
# are "upserted" so that we don't necessarily drop clients that are still
|
153
207
|
# relevant.
|
154
|
-
def reload_pool_nodes
|
208
|
+
def reload_pool_nodes
|
155
209
|
@mutex.synchronize do
|
156
|
-
|
157
|
-
create_multi_node_pool(raise_error)
|
158
|
-
refresh_startup_nodes
|
159
|
-
else
|
160
|
-
create_single_node_pool
|
161
|
-
end
|
210
|
+
reload_pool_nodes_unsync
|
162
211
|
end
|
163
212
|
end
|
164
213
|
|
165
|
-
#
|
214
|
+
# The same as `#reload_pool_nodes`, but doesn't attempt to synchronize on
|
215
|
+
# the mutex. Use this only if you've already got a lock on it.
|
216
|
+
def reload_pool_nodes_unsync
|
217
|
+
if @hosts.is_a?(Array)
|
218
|
+
create_multi_node_pool
|
219
|
+
refresh_startup_nodes
|
220
|
+
else
|
221
|
+
create_single_node_pool
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Refreshes the contents of @hosts based on the hosts currently in
|
166
226
|
# the client pool. This is useful because we may have been told about new
|
167
227
|
# hosts after running `CLUSTER SLOTS`.
|
168
228
|
def refresh_startup_nodes
|
169
|
-
@pool.nodes.each {|node| @
|
170
|
-
@
|
229
|
+
@pool.nodes.each {|node| @hosts.push(node.host_hash) }
|
230
|
+
@hosts.uniq!
|
231
|
+
end
|
232
|
+
|
233
|
+
# Retries an operation @retry_count times for intermittent connection
|
234
|
+
# errors. After exhausting retries, the error that was received on the last
|
235
|
+
# attempt is raised to the user.
|
236
|
+
def retry_intermittent_loop
|
237
|
+
last_error = nil
|
238
|
+
|
239
|
+
for attempt in 0..(@retry_count) do
|
240
|
+
begin
|
241
|
+
yield(attempt)
|
242
|
+
|
243
|
+
# Fall through on any success.
|
244
|
+
return
|
245
|
+
rescue Errno::EACCES, Redis::TimeoutError, Redis::CannotConnectError => e
|
246
|
+
last_error = e
|
247
|
+
|
248
|
+
unless @logger.nil?
|
249
|
+
@logger.error("redis_cluster: Received error: #{e} retries_left=#{@retry_count - attempt}")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# If we ran out of retries (the maximum number may have been set to 0),
|
255
|
+
# surface any error that was thrown back to the caller. We'd otherwise
|
256
|
+
# suppress the error, which would return something quite unexpected.
|
257
|
+
raise last_error
|
171
258
|
end
|
172
259
|
|
173
260
|
end # end client
|
data/lib/redis_cluster/node.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module RedisCluster
|
2
2
|
|
3
3
|
class Node
|
4
|
+
attr_accessor :options
|
5
|
+
|
4
6
|
# slots is a range array: [1..100, 300..500]
|
5
7
|
attr_accessor :slots
|
6
8
|
|
@@ -35,7 +37,7 @@ module RedisCluster
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def connection
|
38
|
-
@connection ||= self.class.redis(
|
40
|
+
@connection ||= self.class.redis(options)
|
39
41
|
end
|
40
42
|
|
41
43
|
def self.redis(options)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_cluster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- wangzc
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
142
|
version: '0'
|
143
143
|
requirements: []
|
144
144
|
rubyforge_project:
|
145
|
-
rubygems_version: 2.6.
|
145
|
+
rubygems_version: 2.6.10
|
146
146
|
signing_key:
|
147
147
|
specification_version: 4
|
148
148
|
summary: redis cluster client
|