redis 4.2.5 → 5.0.7
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/CHANGELOG.md +167 -0
- data/README.md +102 -162
- data/lib/redis/client.rb +84 -589
- data/lib/redis/commands/bitmaps.rb +66 -0
- data/lib/redis/commands/cluster.rb +28 -0
- data/lib/redis/commands/connection.rb +53 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +254 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +437 -0
- data/lib/redis/commands/lists.rb +339 -0
- data/lib/redis/commands/pubsub.rb +54 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +214 -0
- data/lib/redis/commands/sorted_sets.rb +884 -0
- data/lib/redis/commands/streams.rb +402 -0
- data/lib/redis/commands/strings.rb +314 -0
- data/lib/redis/commands/transactions.rb +115 -0
- data/lib/redis/commands.rb +237 -0
- data/lib/redis/distributed.rb +220 -75
- data/lib/redis/errors.rb +15 -41
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +66 -120
- data/lib/redis/subscribe.rb +23 -15
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +110 -3441
- metadata +27 -54
- data/lib/redis/cluster/command.rb +0 -81
- data/lib/redis/cluster/command_loader.rb +0 -34
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -107
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -37
- data/lib/redis/cluster/option.rb +0 -90
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -49
- data/lib/redis/cluster.rb +0 -295
- data/lib/redis/connection/command_helper.rb +0 -39
- data/lib/redis/connection/hiredis.rb +0 -67
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -427
- data/lib/redis/connection/synchrony.rb +0 -146
- data/lib/redis/connection.rb +0 -11
data/lib/redis/hash_ring.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'zlib'
|
4
|
+
require 'digest/md5'
|
4
5
|
|
5
6
|
class Redis
|
6
7
|
class HashRing
|
@@ -25,7 +26,7 @@ class Redis
|
|
25
26
|
def add_node(node)
|
26
27
|
@nodes << node
|
27
28
|
@replicas.times do |i|
|
28
|
-
key =
|
29
|
+
key = server_hash_for("#{node.id}:#{i}")
|
29
30
|
@ring[key] = node
|
30
31
|
@sorted_keys << key
|
31
32
|
end
|
@@ -35,7 +36,7 @@ class Redis
|
|
35
36
|
def remove_node(node)
|
36
37
|
@nodes.reject! { |n| n.id == node.id }
|
37
38
|
@replicas.times do |i|
|
38
|
-
key =
|
39
|
+
key = server_hash_for("#{node.id}:#{i}")
|
39
40
|
@ring.delete(key)
|
40
41
|
@sorted_keys.reject! { |k| k == key }
|
41
42
|
end
|
@@ -43,47 +44,46 @@ class Redis
|
|
43
44
|
|
44
45
|
# get the node in the hash ring for this key
|
45
46
|
def get_node(key)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def get_node_pos(key)
|
50
|
-
return [nil, nil] if @ring.empty?
|
51
|
-
|
52
|
-
crc = Zlib.crc32(key)
|
53
|
-
idx = HashRing.binary_search(@sorted_keys, crc)
|
54
|
-
[@ring[@sorted_keys[idx]], idx]
|
47
|
+
hash = hash_for(key)
|
48
|
+
idx = binary_search(@sorted_keys, hash)
|
49
|
+
@ring[@sorted_keys[idx]]
|
55
50
|
end
|
56
51
|
|
57
52
|
def iter_nodes(key)
|
58
53
|
return [nil, nil] if @ring.empty?
|
59
54
|
|
60
|
-
|
55
|
+
crc = hash_for(key)
|
56
|
+
pos = binary_search(@sorted_keys, crc)
|
61
57
|
@ring.size.times do |n|
|
62
58
|
yield @ring[@sorted_keys[(pos + n) % @ring.size]]
|
63
59
|
end
|
64
60
|
end
|
65
61
|
|
62
|
+
private
|
63
|
+
|
64
|
+
def hash_for(key)
|
65
|
+
Zlib.crc32(key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def server_hash_for(key)
|
69
|
+
Digest::MD5.digest(key).unpack1("L>")
|
70
|
+
end
|
71
|
+
|
66
72
|
# Find the closest index in HashRing with value <= the given value
|
67
|
-
def
|
68
|
-
upper = ary.size
|
73
|
+
def binary_search(ary, value)
|
74
|
+
upper = ary.size
|
69
75
|
lower = 0
|
70
|
-
idx = 0
|
71
|
-
|
72
|
-
while lower <= upper
|
73
|
-
idx = (lower + upper) / 2
|
74
|
-
comp = ary[idx] <=> value
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
upper =
|
77
|
+
while lower < upper
|
78
|
+
mid = (lower + upper) / 2
|
79
|
+
if ary[mid] > value
|
80
|
+
upper = mid
|
80
81
|
else
|
81
|
-
lower =
|
82
|
+
lower = mid + 1
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
85
|
-
upper
|
86
|
-
upper
|
86
|
+
upper - 1
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
data/lib/redis/pipeline.rb
CHANGED
@@ -1,126 +1,72 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "delegate"
|
4
|
+
|
3
5
|
class Redis
|
4
|
-
class
|
6
|
+
class PipelinedConnection
|
5
7
|
attr_accessor :db
|
6
|
-
attr_reader :client
|
7
|
-
|
8
|
-
attr :futures
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@shutdown = false
|
14
|
-
@futures = []
|
9
|
+
def initialize(pipeline, futures = [])
|
10
|
+
@pipeline = pipeline
|
11
|
+
@futures = futures
|
15
12
|
end
|
16
13
|
|
17
|
-
|
18
|
-
client.timeout
|
19
|
-
end
|
14
|
+
include Commands
|
20
15
|
|
21
|
-
def
|
22
|
-
|
16
|
+
def pipelined
|
17
|
+
yield self
|
23
18
|
end
|
24
19
|
|
25
|
-
def
|
26
|
-
|
20
|
+
def multi
|
21
|
+
transaction = MultiConnection.new(@pipeline, @futures)
|
22
|
+
send_command([:multi])
|
23
|
+
size = @futures.size
|
24
|
+
yield transaction
|
25
|
+
multi_future = MultiFuture.new(@futures[size..-1])
|
26
|
+
@pipeline.call_v([:exec]) do |result|
|
27
|
+
multi_future._set(result)
|
28
|
+
end
|
29
|
+
@futures << multi_future
|
30
|
+
multi_future
|
27
31
|
end
|
28
32
|
|
29
|
-
|
30
|
-
@shutdown
|
31
|
-
end
|
33
|
+
private
|
32
34
|
|
33
|
-
def
|
34
|
-
|
35
|
+
def synchronize
|
36
|
+
yield self
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
def send_command(command, &block)
|
40
|
+
future = Future.new(command, block)
|
41
|
+
@pipeline.call_v(command) do |result|
|
42
|
+
future._set(result)
|
43
|
+
end
|
42
44
|
@futures << future
|
43
45
|
future
|
44
46
|
end
|
45
47
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def call_pipeline(pipeline)
|
51
|
-
@shutdown = true if pipeline.shutdown?
|
52
|
-
@futures.concat(pipeline.futures)
|
53
|
-
@db = pipeline.db
|
54
|
-
nil
|
55
|
-
end
|
56
|
-
|
57
|
-
def commands
|
58
|
-
@futures.map(&:_command)
|
59
|
-
end
|
60
|
-
|
61
|
-
def timeouts
|
62
|
-
@futures.map(&:timeout)
|
63
|
-
end
|
64
|
-
|
65
|
-
def with_reconnect(val = true)
|
66
|
-
@with_reconnect = false unless val
|
67
|
-
yield
|
68
|
-
end
|
69
|
-
|
70
|
-
def without_reconnect(&blk)
|
71
|
-
with_reconnect(false, &blk)
|
72
|
-
end
|
73
|
-
|
74
|
-
def finish(replies, &blk)
|
75
|
-
if blk
|
76
|
-
futures.each_with_index.map do |future, i|
|
77
|
-
future._set(blk.call(replies[i]))
|
78
|
-
end
|
79
|
-
else
|
80
|
-
futures.each_with_index.map do |future, i|
|
81
|
-
future._set(replies[i])
|
82
|
-
end
|
48
|
+
def send_blocking_command(command, timeout, &block)
|
49
|
+
future = Future.new(command, block)
|
50
|
+
@pipeline.blocking_call_v(timeout, command) do |result|
|
51
|
+
future._set(result)
|
83
52
|
end
|
53
|
+
@futures << future
|
54
|
+
future
|
84
55
|
end
|
56
|
+
end
|
85
57
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
return if exec.nil? # The transaction failed because of WATCH.
|
91
|
-
|
92
|
-
# EXEC command failed.
|
93
|
-
raise exec if exec.is_a?(CommandError)
|
94
|
-
|
95
|
-
if exec.size < futures.size
|
96
|
-
# Some command wasn't recognized by Redis.
|
97
|
-
command_error = replies.detect { |r| r.is_a?(CommandError) }
|
98
|
-
raise command_error
|
99
|
-
end
|
58
|
+
class MultiConnection < PipelinedConnection
|
59
|
+
def multi
|
60
|
+
raise Redis::Error, "Can't nest multi transaction"
|
61
|
+
end
|
100
62
|
|
101
|
-
|
102
|
-
# Because an EXEC returns nested replies, hiredis won't be able to
|
103
|
-
# convert an error reply to a CommandError instance itself. This is
|
104
|
-
# specific to MULTI/EXEC, so we solve this here.
|
105
|
-
reply.is_a?(::RuntimeError) ? CommandError.new(reply.message) : reply
|
106
|
-
end
|
107
|
-
end
|
63
|
+
private
|
108
64
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def commands
|
118
|
-
if empty?
|
119
|
-
[]
|
120
|
-
else
|
121
|
-
[[:multi]] + super + [[:exec]]
|
122
|
-
end
|
123
|
-
end
|
65
|
+
# Blocking commands inside transaction behave like non-blocking.
|
66
|
+
# It shouldn't be done though.
|
67
|
+
# https://redis.io/commands/blpop/#blpop-inside-a-multi--exec-transaction
|
68
|
+
def send_blocking_command(command, _timeout, &block)
|
69
|
+
send_command(command, &block)
|
124
70
|
end
|
125
71
|
end
|
126
72
|
|
@@ -133,23 +79,10 @@ class Redis
|
|
133
79
|
class Future < BasicObject
|
134
80
|
FutureNotReady = ::Redis::FutureNotReady.new
|
135
81
|
|
136
|
-
|
137
|
-
|
138
|
-
def initialize(command, transformation, timeout)
|
82
|
+
def initialize(command, coerce)
|
139
83
|
@command = command
|
140
|
-
@transformation = transformation
|
141
|
-
@timeout = timeout
|
142
84
|
@object = FutureNotReady
|
143
|
-
|
144
|
-
|
145
|
-
def ==(_other)
|
146
|
-
message = +"The methods == and != are deprecated for Redis::Future and will be removed in 4.2.0"
|
147
|
-
message << " - You probably meant to call .value == or .value !="
|
148
|
-
message << " (#{::Kernel.caller(1, 1).first})\n"
|
149
|
-
|
150
|
-
::Kernel.warn(message)
|
151
|
-
|
152
|
-
super
|
85
|
+
@coerce = coerce
|
153
86
|
end
|
154
87
|
|
155
88
|
def inspect
|
@@ -157,16 +90,12 @@ class Redis
|
|
157
90
|
end
|
158
91
|
|
159
92
|
def _set(object)
|
160
|
-
@object = @
|
93
|
+
@object = @coerce ? @coerce.call(object) : object
|
161
94
|
value
|
162
95
|
end
|
163
96
|
|
164
|
-
def _command
|
165
|
-
@command
|
166
|
-
end
|
167
|
-
|
168
97
|
def value
|
169
|
-
::Kernel.raise(@object) if @object.is_a?(::
|
98
|
+
::Kernel.raise(@object) if @object.is_a?(::StandardError)
|
170
99
|
@object
|
171
100
|
end
|
172
101
|
|
@@ -178,4 +107,21 @@ class Redis
|
|
178
107
|
Future
|
179
108
|
end
|
180
109
|
end
|
110
|
+
|
111
|
+
class MultiFuture < Future
|
112
|
+
def initialize(futures)
|
113
|
+
@futures = futures
|
114
|
+
@command = [:exec]
|
115
|
+
@object = FutureNotReady
|
116
|
+
end
|
117
|
+
|
118
|
+
def _set(replies)
|
119
|
+
if replies
|
120
|
+
@futures.each_with_index do |future, index|
|
121
|
+
future._set(replies[index])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
@object = replies
|
125
|
+
end
|
126
|
+
end
|
181
127
|
end
|
data/lib/redis/subscribe.rb
CHANGED
@@ -4,10 +4,13 @@ class Redis
|
|
4
4
|
class SubscribedClient
|
5
5
|
def initialize(client)
|
6
6
|
@client = client
|
7
|
+
@write_monitor = Monitor.new
|
7
8
|
end
|
8
9
|
|
9
|
-
def
|
10
|
-
@
|
10
|
+
def call_v(command)
|
11
|
+
@write_monitor.synchronize do
|
12
|
+
@client.call_v(command)
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
def subscribe(*channels, &block)
|
@@ -27,11 +30,15 @@ class Redis
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def unsubscribe(*channels)
|
30
|
-
|
33
|
+
call_v([:unsubscribe, *channels])
|
31
34
|
end
|
32
35
|
|
33
36
|
def punsubscribe(*channels)
|
34
|
-
|
37
|
+
call_v([:punsubscribe, *channels])
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
@client.close
|
35
42
|
end
|
36
43
|
|
37
44
|
protected
|
@@ -39,13 +46,17 @@ class Redis
|
|
39
46
|
def subscription(start, stop, channels, block, timeout = 0)
|
40
47
|
sub = Subscription.new(&block)
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
call_v([start, *channels])
|
50
|
+
while event = @client.next_event(timeout)
|
51
|
+
if event.is_a?(::RedisClient::CommandError)
|
52
|
+
raise Client::ERROR_MAPPING.fetch(event.class), event.message
|
53
|
+
end
|
54
|
+
|
55
|
+
type, *rest = event
|
56
|
+
if callback = sub.callbacks[type]
|
57
|
+
callback.call(*rest)
|
58
|
+
end
|
59
|
+
break if type == stop && rest.last == 0
|
49
60
|
end
|
50
61
|
# No need to unsubscribe here. The real client closes the connection
|
51
62
|
# whenever an exception is raised (see #ensure_connected).
|
@@ -56,10 +67,7 @@ class Redis
|
|
56
67
|
attr :callbacks
|
57
68
|
|
58
69
|
def initialize
|
59
|
-
@callbacks =
|
60
|
-
hash[key] = ->(*_) {}
|
61
|
-
end
|
62
|
-
|
70
|
+
@callbacks = {}
|
63
71
|
yield(self)
|
64
72
|
end
|
65
73
|
|
data/lib/redis/version.rb
CHANGED