redis-cluster 0.0.9 → 1.0.0.pre.rc.1
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.
- checksums.yaml +4 -4
- data/README.md +70 -4
- data/lib/redis-cluster.rb +44 -34
- data/lib/redis_cluster/client.rb +13 -1
- data/lib/redis_cluster/cluster.rb +47 -29
- data/lib/redis_cluster/function/hash.rb +1 -0
- data/lib/redis_cluster/function/key.rb +1 -0
- data/lib/redis_cluster/function/scan.rb +2 -1
- data/lib/redis_cluster/function/set.rb +1 -0
- data/lib/redis_cluster/function/sorted_set.rb +1 -0
- data/lib/redis_cluster/function/string.rb +1 -0
- data/lib/redis_cluster/future.rb +1 -0
- data/lib/redis_cluster/middlewares.rb +27 -0
- data/lib/redis_cluster/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dd6f66183d627b2454e9e7ba629274ba32a1e78
|
4
|
+
data.tar.gz: 3519af542e7ed85abb0593bccbb9dba0f451762d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8c89e900077de31226af2067769576136735356388dd69d4f528697a3f67d4e2fa6685def24ba27a24a5f57992cf91eca2002a61c49e9baa4c94ae7c582c100
|
7
|
+
data.tar.gz: 74921e593e6a1338e2bcc382f6e27718b3d8a4808dd7ed8d589d539b3d3f9fce72ccac5bd0f0d002f1b142b2b04a0fe2ef3966f77fa92a68165ed1a2c01b0d6d
|
data/README.md
CHANGED
@@ -33,6 +33,10 @@ SRE Bukalapak
|
|
33
33
|
redis_opts: { timeout: 5, connect_timeout: 1 },
|
34
34
|
cluster_opts: { force_cluster: false, read_mode: :master_slave, silent: true, logger: Logger.new }
|
35
35
|
)
|
36
|
+
redis.middlewares.register(:commit) do |*args, &block|
|
37
|
+
puts "this is RedisCluster middlewares"
|
38
|
+
block.call
|
39
|
+
end
|
36
40
|
````
|
37
41
|
|
38
42
|
### Development Guide
|
@@ -41,7 +45,7 @@ SRE Bukalapak
|
|
41
45
|
See [here](https://rvm.io/rvm/install) to install `rvm`.
|
42
46
|
And run these commands to install `bundler` and other dependencies
|
43
47
|
|
44
|
-
````
|
48
|
+
````sh
|
45
49
|
gem install bundler
|
46
50
|
bundle install
|
47
51
|
````
|
@@ -53,20 +57,20 @@ SRE Bukalapak
|
|
53
57
|
|
54
58
|
4. Make your change and it's test.
|
55
59
|
|
56
|
-
````
|
60
|
+
````sh
|
57
61
|
vim lib/**.rb
|
58
62
|
vim spec/**_spec.rb
|
59
63
|
````
|
60
64
|
|
61
65
|
5. Optionally, run the test in your local
|
62
66
|
|
63
|
-
````
|
67
|
+
````sh
|
64
68
|
rake # run all test and lint
|
65
69
|
````
|
66
70
|
|
67
71
|
6. Commit and push your change to upstream
|
68
72
|
|
69
|
-
````
|
73
|
+
````sh
|
70
74
|
git commit -m "message"
|
71
75
|
git push # add "--set-upstream branch_name" after "git push" if you haven't set the upstream
|
72
76
|
````
|
@@ -91,6 +95,68 @@ Option for RedisCluster.
|
|
91
95
|
- `silent`: whether or not RedisCluster will raise error.
|
92
96
|
- `logger`: if specified. RedisCluster will log all of RedisCluster errors here.
|
93
97
|
|
98
|
+
#### Middlewares
|
99
|
+
|
100
|
+
Middlewares are hooks that RedisCluster provide to observe RedisCluster events. To register a middlewares, provide callable object (object that respond to call)
|
101
|
+
or give block in register method. Middlewares must give block result as return value.
|
102
|
+
````ruby
|
103
|
+
class Callable
|
104
|
+
call
|
105
|
+
start = Time.now
|
106
|
+
yield
|
107
|
+
rescue StandardError => e
|
108
|
+
raise e
|
109
|
+
ensure
|
110
|
+
Metrics.publish(elapsed_time: Time.now - start)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
redis.middlewares.register(:commit, Callable.new)
|
114
|
+
|
115
|
+
redis.middlewares.register(:commit) do |*args, &block|
|
116
|
+
begin
|
117
|
+
res = block.call
|
118
|
+
rescue StandardError => e
|
119
|
+
Log.warn('failed')
|
120
|
+
raise e
|
121
|
+
end
|
122
|
+
Log.info('success')
|
123
|
+
res
|
124
|
+
end
|
125
|
+
````
|
126
|
+
|
127
|
+
Currently there are 3 events that RedisCluster publish.
|
128
|
+
- `:commit`
|
129
|
+
RedisCluster will fire `:commit` events when RedisCluster::Client call redis server. It give queue of command as arguments.
|
130
|
+
````ruby
|
131
|
+
redis.middlewares.register(:commit) do |queues, &block|
|
132
|
+
puts 'this is :commit events'
|
133
|
+
puts "first command: #{queues.first.first}
|
134
|
+
puts "last command: #{queues.last.first}
|
135
|
+
block.call
|
136
|
+
end
|
137
|
+
````
|
138
|
+
- `:call`
|
139
|
+
This events is fired when command is issued in RedisCluster client before any load balancing is done. It give call arguments as arguments
|
140
|
+
````ruby
|
141
|
+
redis.middlewares.register(:call) do |keys, command, opts = {}, &block|
|
142
|
+
puts "keys to load balance: #{keys}"
|
143
|
+
puts "redis command: #{command.first}"
|
144
|
+
block.call
|
145
|
+
end
|
146
|
+
redis.get('something')
|
147
|
+
# Output:
|
148
|
+
# keys to load balance: something
|
149
|
+
# redis command: get
|
150
|
+
````
|
151
|
+
- `:pipelined`
|
152
|
+
This events is fired when pipelined method is called from redis client. It does not give any arguments
|
153
|
+
````ruby
|
154
|
+
redis.middlewares.register(:pipelined) do |&block|
|
155
|
+
puts 'pipelined is called'
|
156
|
+
block.call
|
157
|
+
end
|
158
|
+
````
|
159
|
+
|
94
160
|
### Limitation
|
95
161
|
|
96
162
|
All multi keys operation, cluster command, multi-exec, and some commands are not supported.
|
data/lib/redis-cluster.rb
CHANGED
@@ -1,40 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'redis'
|
3
4
|
|
4
5
|
require_relative 'redis_cluster/cluster'
|
5
6
|
require_relative 'redis_cluster/client'
|
6
7
|
require_relative 'redis_cluster/future'
|
7
8
|
require_relative 'redis_cluster/function'
|
9
|
+
require_relative 'redis_cluster/middlewares'
|
8
10
|
|
9
11
|
# RedisCluster is a client for redis-cluster *huh*
|
10
12
|
class RedisCluster
|
11
13
|
include MonitorMixin
|
12
14
|
include Function
|
13
15
|
|
14
|
-
attr_reader :cluster, :
|
16
|
+
attr_reader :cluster, :cluster_opts, :redis_opts, :middlewares
|
15
17
|
|
16
18
|
def initialize(seeds, redis_opts: nil, cluster_opts: nil)
|
17
|
-
@
|
18
|
-
|
19
|
-
@
|
19
|
+
@cluster_opts = cluster_opts || {}
|
20
|
+
@redis_opts = redis_opts || {}
|
21
|
+
@middlewares = Middlewares.new
|
22
|
+
|
23
|
+
client_creater = method(:create_client)
|
24
|
+
@cluster = Cluster.new(seeds, cluster_opts, &client_creater)
|
20
25
|
|
21
26
|
super()
|
22
27
|
end
|
23
28
|
|
24
29
|
def logger
|
25
|
-
|
30
|
+
cluster_opts[:logger]
|
26
31
|
end
|
27
32
|
|
28
33
|
def silent?
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
def read_mode
|
33
|
-
options[:read_mode] || :master
|
34
|
-
end
|
35
|
-
|
36
|
-
def force_cluster?
|
37
|
-
options[:force_cluster]
|
34
|
+
cluster_opts[:silent]
|
38
35
|
end
|
39
36
|
|
40
37
|
def connected?
|
@@ -49,9 +46,25 @@ class RedisCluster
|
|
49
46
|
!@pipeline.nil?
|
50
47
|
end
|
51
48
|
|
52
|
-
def call(
|
49
|
+
def call(*args, &block)
|
50
|
+
middlewares.invoke(:call, *args) do
|
51
|
+
_call(*args, &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def pipelined(*args, &block)
|
56
|
+
middlewares.invoke(:pipelined, *args) do
|
57
|
+
_pipelined(*args, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
NOOP = ->(v){ v }
|
64
|
+
|
65
|
+
def _call(keys, command, opts = {})
|
53
66
|
opts[:transform] ||= NOOP
|
54
|
-
slot = slot_for(
|
67
|
+
slot = cluster.slot_for(keys)
|
55
68
|
|
56
69
|
safely do
|
57
70
|
if pipeline?
|
@@ -62,7 +75,7 @@ class RedisCluster
|
|
62
75
|
end
|
63
76
|
end
|
64
77
|
|
65
|
-
def
|
78
|
+
def _pipelined
|
66
79
|
safely do
|
67
80
|
return yield if pipeline?
|
68
81
|
|
@@ -94,11 +107,6 @@ class RedisCluster
|
|
94
107
|
end
|
95
108
|
end
|
96
109
|
|
97
|
-
private
|
98
|
-
|
99
|
-
NOOP = ->(v){ v }
|
100
|
-
CROSSSLOT_ERROR = Redis::CommandError.new("CROSSSLOT Keys in request don't hash to the same slot")
|
101
|
-
|
102
110
|
def safely
|
103
111
|
synchronize{ yield } if block_given?
|
104
112
|
rescue StandardError => e
|
@@ -106,17 +114,12 @@ class RedisCluster
|
|
106
114
|
raise e unless silent?
|
107
115
|
end
|
108
116
|
|
109
|
-
def slot_for(keys)
|
110
|
-
slot = keys.map{ |k| cluster.slot_for(k) }.uniq
|
111
|
-
slot.size == 1 ? slot.first : ( raise CROSSSLOT_ERROR )
|
112
|
-
end
|
113
|
-
|
114
117
|
def call_immediately(slot, command, transform:, read: false)
|
115
118
|
try = 3
|
116
119
|
asking = false
|
117
120
|
reply = nil
|
118
|
-
mode = read ?
|
119
|
-
client = cluster.
|
121
|
+
mode = read ? :read : :write
|
122
|
+
client = cluster.client_for(mode, slot)
|
120
123
|
|
121
124
|
while try.positive?
|
122
125
|
begin
|
@@ -134,7 +137,7 @@ class RedisCluster
|
|
134
137
|
rescue Redis::CannotConnectError => e
|
135
138
|
asking = false
|
136
139
|
cluster.reset
|
137
|
-
client = cluster.
|
140
|
+
client = cluster.client_for(mode, slot)
|
138
141
|
reply = e
|
139
142
|
end
|
140
143
|
end
|
@@ -151,11 +154,11 @@ class RedisCluster
|
|
151
154
|
def map_pipeline(pipe)
|
152
155
|
futures = ::Hash.new{ |h, k| h[k] = [] }
|
153
156
|
pipe.each do |future|
|
154
|
-
url = future.url || cluster.
|
157
|
+
url = future.url || cluster.client_for(:write, future.slot).url
|
155
158
|
futures[url] << future
|
156
159
|
end
|
157
160
|
|
158
|
-
|
161
|
+
futures
|
159
162
|
end
|
160
163
|
|
161
164
|
def do_pipelined(url, futures)
|
@@ -192,12 +195,12 @@ class RedisCluster
|
|
192
195
|
leftover << future
|
193
196
|
end
|
194
197
|
|
195
|
-
|
198
|
+
[leftover, moved]
|
196
199
|
rescue Redis::CannotConnectError
|
197
200
|
# reset url and asking when connection refused
|
198
201
|
futures.each{ |f| f.url = nil; f.asking = false }
|
199
202
|
|
200
|
-
|
203
|
+
[futures, true]
|
201
204
|
end
|
202
205
|
|
203
206
|
def scan_reply(reply)
|
@@ -210,4 +213,11 @@ class RedisCluster
|
|
210
213
|
raise reply
|
211
214
|
end
|
212
215
|
end
|
216
|
+
|
217
|
+
def create_client(url)
|
218
|
+
host, port = url.split(':', 2)
|
219
|
+
Client.new(redis_opts.merge(host: host, port: port)).tap do |c|
|
220
|
+
c.middlewares = middlewares
|
221
|
+
end
|
222
|
+
end
|
213
223
|
end
|
data/lib/redis_cluster/client.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'redis'
|
3
4
|
|
4
5
|
require_relative 'version'
|
@@ -9,6 +10,7 @@ class RedisCluster
|
|
9
10
|
# useful addition
|
10
11
|
class Client
|
11
12
|
attr_reader :client, :queue, :url
|
13
|
+
attr_accessor :middlewares
|
12
14
|
|
13
15
|
def initialize(opts)
|
14
16
|
@client = Redis::Client.new(opts)
|
@@ -38,6 +40,16 @@ class RedisCluster
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def commit
|
43
|
+
return _commit unless middlewares
|
44
|
+
|
45
|
+
middlewares.invoke(:commit, queue.dup) do
|
46
|
+
_commit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def _commit
|
41
53
|
return nil if queue.empty?
|
42
54
|
|
43
55
|
result = Array.new(queue.size)
|
@@ -48,7 +60,7 @@ class RedisCluster
|
|
48
60
|
end
|
49
61
|
@queue = []
|
50
62
|
|
51
|
-
|
63
|
+
result
|
52
64
|
end
|
53
65
|
end
|
54
66
|
end
|
@@ -1,49 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'client'
|
3
4
|
|
4
5
|
class RedisCluster
|
5
6
|
|
6
7
|
# Cluster implement redis cluster logic for client.
|
7
8
|
class Cluster
|
8
|
-
attr_reader :options, :slots, :clients, :replicas
|
9
|
+
attr_reader :options, :slots, :clients, :replicas, :client_creater
|
9
10
|
|
10
11
|
HASH_SLOTS = 16_384
|
12
|
+
CROSSSLOT_ERROR = Redis::CommandError.new("CROSSSLOT Keys in request don't hash to the same slot")
|
11
13
|
|
12
|
-
def initialize(seeds,
|
13
|
-
@options =
|
14
|
+
def initialize(seeds, cluster_opts = {}, &block)
|
15
|
+
@options = cluster_opts
|
14
16
|
@slots = []
|
15
17
|
@clients = {}
|
16
18
|
@replicas = nil
|
19
|
+
@client_creater = block
|
17
20
|
|
18
21
|
init_client(seeds)
|
19
22
|
end
|
20
23
|
|
21
24
|
def force_cluster?
|
22
|
-
options[:force_cluster]
|
25
|
+
options[:force_cluster] || false
|
23
26
|
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
def slot_for(key)
|
28
|
-
key = key.to_s
|
29
|
-
if (s = key.index('{'))
|
30
|
-
if (e = key.index('}', s + 1)) && e != s+1
|
31
|
-
key = key[s+1..e-1]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
crc16(key) % HASH_SLOTS
|
28
|
+
def read_mode
|
29
|
+
options[:read_mode] || :master
|
35
30
|
end
|
36
31
|
|
37
|
-
def
|
38
|
-
|
32
|
+
def slot_for(keys)
|
33
|
+
slot = [keys].flatten.map{ |k| _slot_for(k) }.uniq
|
34
|
+
slot.size == 1 ? slot.first : (raise CROSSSLOT_ERROR)
|
39
35
|
end
|
40
36
|
|
41
|
-
def
|
42
|
-
|
43
|
-
end
|
37
|
+
def client_for(operation, slot)
|
38
|
+
mode = operation == :read ? read_mode : :master
|
44
39
|
|
45
|
-
|
46
|
-
|
40
|
+
case mode
|
41
|
+
when :master
|
42
|
+
slots[slot].first
|
43
|
+
when :slave
|
44
|
+
slots[slot][1..-1].sample || slots[slot].first
|
45
|
+
when :master_slave
|
46
|
+
slots[slot].sample
|
47
|
+
end
|
47
48
|
end
|
48
49
|
|
49
50
|
def close
|
@@ -66,7 +67,7 @@ class RedisCluster
|
|
66
67
|
slots_and_clients(client)
|
67
68
|
rescue StandardError => e
|
68
69
|
clients.delete(client.url)
|
69
|
-
try.positive? ? retry : (
|
70
|
+
try.positive? ? retry : (raise e)
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
@@ -74,12 +75,16 @@ class RedisCluster
|
|
74
75
|
clients[url] ||= create_client(url)
|
75
76
|
end
|
76
77
|
|
78
|
+
def inspect
|
79
|
+
"#<RedisCluster cluster v#{RedisCluster::VERSION}>"
|
80
|
+
end
|
81
|
+
|
77
82
|
private
|
78
83
|
|
79
84
|
def slots_and_clients(client)
|
80
85
|
replicas = ::Hash.new{ |h, k| h[k] = [] }
|
81
86
|
|
82
|
-
result = client.call([
|
87
|
+
result = client.call(%i[cluster slots])
|
83
88
|
if result.is_a?(StandardError)
|
84
89
|
if result.message.eql?('ERR This instance has cluster support disabled') &&
|
85
90
|
!force_cluster?
|
@@ -124,11 +129,12 @@ class RedisCluster
|
|
124
129
|
end
|
125
130
|
|
126
131
|
def create_client(url)
|
127
|
-
|
128
|
-
|
129
|
-
|
132
|
+
if client_creater
|
133
|
+
client_creater.call(url)
|
134
|
+
else
|
135
|
+
host, port = url.split(':', 2)
|
136
|
+
Client.new(host: host, port: port)
|
130
137
|
end
|
131
|
-
Client.new(client_options.merge(host: host, port: port))
|
132
138
|
end
|
133
139
|
|
134
140
|
# -----------------------------------------------------------------------------
|
@@ -152,11 +158,23 @@ class RedisCluster
|
|
152
158
|
def crc16(bytes)
|
153
159
|
crc = 0
|
154
160
|
bytes.each_byte do |b|
|
155
|
-
crc = ((crc<<8) & 0xffff) ^ XMODEM_CRC16_LOOKUP[((crc>>8)^b) & 0xff]
|
161
|
+
crc = ((crc << 8) & 0xffff) ^ XMODEM_CRC16_LOOKUP[((crc >> 8) ^ b) & 0xff]
|
156
162
|
end
|
157
163
|
crc
|
158
164
|
end
|
159
165
|
|
166
|
+
# Return Redis::Client for a given key.
|
167
|
+
# Modified from https://github.com/antirez/redis-rb-cluster/blob/master/cluster.rb#L104-L117
|
168
|
+
def _slot_for(key)
|
169
|
+
key = key.to_s
|
170
|
+
if (s = key.index('{'))
|
171
|
+
if (e = key.index('}', s + 1)) && e != s + 1
|
172
|
+
key = key[s + 1..e - 1]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
crc16(key) % HASH_SLOTS
|
176
|
+
end
|
177
|
+
|
160
178
|
XMODEM_CRC16_LOOKUP = [
|
161
179
|
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
162
180
|
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
@@ -190,6 +208,6 @@ class RedisCluster
|
|
190
208
|
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
191
209
|
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
192
210
|
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
193
|
-
]
|
211
|
+
].freeze
|
194
212
|
end
|
195
213
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'redis'
|
3
4
|
|
4
5
|
class RedisCluster
|
@@ -73,7 +74,7 @@ class RedisCluster
|
|
73
74
|
# @param [Hash] options
|
74
75
|
# - `:match => String`: only return keys matching the pattern
|
75
76
|
# - `:count => Integer`: return count keys at most per iteration
|
76
|
-
[
|
77
|
+
%i[zscan hscan sscan].each do |method|
|
77
78
|
define_method "#{method}_each" do |key, options = {}, &block|
|
78
79
|
return if block.nil?
|
79
80
|
|
data/lib/redis_cluster/future.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RedisCluster
|
4
|
+
|
5
|
+
# Middlewares is collection for RedisCluster middleware.
|
6
|
+
class Middlewares
|
7
|
+
attr_reader :middlewares
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@middlewares = Hash.new{ |h, k| h[k] = [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def register(name, callable = nil, &block)
|
14
|
+
return if !callable && !block_given?
|
15
|
+
|
16
|
+
middlewares[name] << (callable || block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def invoke(name, *args, &block)
|
20
|
+
callback = middlewares[name].reduce(block) do |acc, obs|
|
21
|
+
proc{ obs.call(*args, &acc) }
|
22
|
+
end
|
23
|
+
|
24
|
+
callback.call
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
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.0.
|
4
|
+
version: 1.0.0.pre.rc.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bukalapak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- lib/redis_cluster/function/sorted_set.rb
|
45
45
|
- lib/redis_cluster/function/string.rb
|
46
46
|
- lib/redis_cluster/future.rb
|
47
|
+
- lib/redis_cluster/middlewares.rb
|
47
48
|
- lib/redis_cluster/version.rb
|
48
49
|
homepage: https://github.com/bukalapak/redis-cluster
|
49
50
|
licenses:
|
@@ -60,9 +61,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
61
|
version: '0'
|
61
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
63
|
requirements:
|
63
|
-
- - "
|
64
|
+
- - ">"
|
64
65
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
66
|
+
version: 1.3.1
|
66
67
|
requirements: []
|
67
68
|
rubyforge_project:
|
68
69
|
rubygems_version: 2.5.1
|