redis 4.5.1 → 4.6.0
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 +51 -0
- data/README.md +10 -10
- data/lib/redis/client.rb +14 -13
- data/lib/redis/cluster/command.rb +4 -6
- data/lib/redis/cluster/node.rb +12 -0
- data/lib/redis/cluster.rb +24 -0
- data/lib/redis/commands/bitmaps.rb +63 -0
- data/lib/redis/commands/cluster.rb +45 -0
- data/lib/redis/commands/connection.rb +58 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +251 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +411 -0
- data/lib/redis/commands/lists.rb +289 -0
- data/lib/redis/commands/pubsub.rb +72 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +207 -0
- data/lib/redis/commands/sorted_sets.rb +804 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +92 -0
- data/lib/redis/commands.rb +242 -0
- data/lib/redis/connection/hiredis.rb +3 -2
- data/lib/redis/connection/ruby.rb +8 -5
- data/lib/redis/connection/synchrony.rb +10 -8
- data/lib/redis/connection.rb +1 -1
- data/lib/redis/distributed.rb +46 -9
- data/lib/redis/pipeline.rb +95 -2
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +133 -3675
- metadata +21 -4
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module Commands
|
5
|
+
module Transactions
|
6
|
+
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
7
|
+
#
|
8
|
+
# Using a block is optional, but is necessary for thread-safety.
|
9
|
+
#
|
10
|
+
# An `#unwatch` is automatically issued if an exception is raised within the
|
11
|
+
# block that is a subclass of StandardError and is not a ConnectionError.
|
12
|
+
#
|
13
|
+
# @example With a block
|
14
|
+
# redis.watch("key") do
|
15
|
+
# if redis.get("key") == "some value"
|
16
|
+
# redis.multi do |multi|
|
17
|
+
# multi.set("key", "other value")
|
18
|
+
# multi.incr("counter")
|
19
|
+
# end
|
20
|
+
# else
|
21
|
+
# redis.unwatch
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# # => ["OK", 6]
|
25
|
+
#
|
26
|
+
# @example Without a block
|
27
|
+
# redis.watch("key")
|
28
|
+
# # => "OK"
|
29
|
+
#
|
30
|
+
# @param [String, Array<String>] keys one or more keys to watch
|
31
|
+
# @return [Object] if using a block, returns the return value of the block
|
32
|
+
# @return [String] if not using a block, returns `OK`
|
33
|
+
#
|
34
|
+
# @see #unwatch
|
35
|
+
# @see #multi
|
36
|
+
def watch(*keys)
|
37
|
+
synchronize do |client|
|
38
|
+
res = client.call([:watch, *keys])
|
39
|
+
|
40
|
+
if block_given?
|
41
|
+
begin
|
42
|
+
yield(self)
|
43
|
+
rescue ConnectionError
|
44
|
+
raise
|
45
|
+
rescue StandardError
|
46
|
+
unwatch
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
else
|
50
|
+
res
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Forget about all watched keys.
|
56
|
+
#
|
57
|
+
# @return [String] `OK`
|
58
|
+
#
|
59
|
+
# @see #watch
|
60
|
+
# @see #multi
|
61
|
+
def unwatch
|
62
|
+
send_command([:unwatch])
|
63
|
+
end
|
64
|
+
|
65
|
+
# Execute all commands issued after MULTI.
|
66
|
+
#
|
67
|
+
# Only call this method when `#multi` was called **without** a block.
|
68
|
+
#
|
69
|
+
# @return [nil, Array<...>]
|
70
|
+
# - when commands were not executed, `nil`
|
71
|
+
# - when commands were executed, an array with their replies
|
72
|
+
#
|
73
|
+
# @see #multi
|
74
|
+
# @see #discard
|
75
|
+
def exec
|
76
|
+
send_command([:exec])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Discard all commands issued after MULTI.
|
80
|
+
#
|
81
|
+
# Only call this method when `#multi` was called **without** a block.
|
82
|
+
#
|
83
|
+
# @return [String] `"OK"`
|
84
|
+
#
|
85
|
+
# @see #multi
|
86
|
+
# @see #exec
|
87
|
+
def discard
|
88
|
+
send_command([:discard])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redis/commands/bitmaps"
|
4
|
+
require "redis/commands/cluster"
|
5
|
+
require "redis/commands/connection"
|
6
|
+
require "redis/commands/geo"
|
7
|
+
require "redis/commands/hashes"
|
8
|
+
require "redis/commands/hyper_log_log"
|
9
|
+
require "redis/commands/keys"
|
10
|
+
require "redis/commands/lists"
|
11
|
+
require "redis/commands/pubsub"
|
12
|
+
require "redis/commands/scripting"
|
13
|
+
require "redis/commands/server"
|
14
|
+
require "redis/commands/sets"
|
15
|
+
require "redis/commands/sorted_sets"
|
16
|
+
require "redis/commands/streams"
|
17
|
+
require "redis/commands/strings"
|
18
|
+
require "redis/commands/transactions"
|
19
|
+
|
20
|
+
class Redis
|
21
|
+
module Commands
|
22
|
+
include Bitmaps
|
23
|
+
include Cluster
|
24
|
+
include Connection
|
25
|
+
include Geo
|
26
|
+
include Hashes
|
27
|
+
include HyperLogLog
|
28
|
+
include Keys
|
29
|
+
include Lists
|
30
|
+
include Pubsub
|
31
|
+
include Scripting
|
32
|
+
include Server
|
33
|
+
include Sets
|
34
|
+
include SortedSets
|
35
|
+
include Streams
|
36
|
+
include Strings
|
37
|
+
include Transactions
|
38
|
+
|
39
|
+
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
40
|
+
# where the method call will return nil. Propagate the nil instead of falsely
|
41
|
+
# returning false.
|
42
|
+
Boolify = lambda { |value|
|
43
|
+
case value
|
44
|
+
when 1
|
45
|
+
true
|
46
|
+
when 0
|
47
|
+
false
|
48
|
+
else
|
49
|
+
value
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
BoolifySet = lambda { |value|
|
54
|
+
case value
|
55
|
+
when "OK"
|
56
|
+
true
|
57
|
+
when nil
|
58
|
+
false
|
59
|
+
else
|
60
|
+
value
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
Hashify = lambda { |value|
|
65
|
+
if value.respond_to?(:each_slice)
|
66
|
+
value.each_slice(2).to_h
|
67
|
+
else
|
68
|
+
value
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
Pairify = lambda { |value|
|
73
|
+
if value.respond_to?(:each_slice)
|
74
|
+
value.each_slice(2).to_a
|
75
|
+
else
|
76
|
+
value
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
Floatify = lambda { |value|
|
81
|
+
case value
|
82
|
+
when "inf"
|
83
|
+
Float::INFINITY
|
84
|
+
when "-inf"
|
85
|
+
-Float::INFINITY
|
86
|
+
when String
|
87
|
+
Float(value)
|
88
|
+
else
|
89
|
+
value
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
FloatifyPairs = lambda { |value|
|
94
|
+
return value unless value.respond_to?(:each_slice)
|
95
|
+
|
96
|
+
value.each_slice(2).map do |member, score|
|
97
|
+
[member, Floatify.call(score)]
|
98
|
+
end
|
99
|
+
}
|
100
|
+
|
101
|
+
HashifyInfo = lambda { |reply|
|
102
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
103
|
+
lines.map! { |line| line.split(':', 2) }
|
104
|
+
lines.compact!
|
105
|
+
lines.to_h
|
106
|
+
}
|
107
|
+
|
108
|
+
HashifyStreams = lambda { |reply|
|
109
|
+
case reply
|
110
|
+
when nil
|
111
|
+
{}
|
112
|
+
else
|
113
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
117
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
118
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
119
|
+
|
120
|
+
HashifyStreamEntries = lambda { |reply|
|
121
|
+
reply.compact.map do |entry_id, values|
|
122
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
123
|
+
end
|
124
|
+
}
|
125
|
+
|
126
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
127
|
+
{
|
128
|
+
'next' => reply[0],
|
129
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
134
|
+
{
|
135
|
+
'next' => reply[0],
|
136
|
+
'entries' => reply[1]
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
HashifyStreamPendings = lambda { |reply|
|
141
|
+
{
|
142
|
+
'size' => reply[0],
|
143
|
+
'min_entry_id' => reply[1],
|
144
|
+
'max_entry_id' => reply[2],
|
145
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
150
|
+
reply.map do |arr|
|
151
|
+
{
|
152
|
+
'entry_id' => arr[0],
|
153
|
+
'consumer' => arr[1],
|
154
|
+
'elapsed' => arr[2],
|
155
|
+
'count' => arr[3]
|
156
|
+
}
|
157
|
+
end
|
158
|
+
}
|
159
|
+
|
160
|
+
HashifyClusterNodeInfo = lambda { |str|
|
161
|
+
arr = str.split(' ')
|
162
|
+
{
|
163
|
+
'node_id' => arr[0],
|
164
|
+
'ip_port' => arr[1],
|
165
|
+
'flags' => arr[2].split(','),
|
166
|
+
'master_node_id' => arr[3],
|
167
|
+
'ping_sent' => arr[4],
|
168
|
+
'pong_recv' => arr[5],
|
169
|
+
'config_epoch' => arr[6],
|
170
|
+
'link_state' => arr[7],
|
171
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
HashifyClusterSlots = lambda { |reply|
|
176
|
+
reply.map do |arr|
|
177
|
+
first_slot, last_slot = arr[0..1]
|
178
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
179
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
180
|
+
{
|
181
|
+
'start_slot' => first_slot,
|
182
|
+
'end_slot' => last_slot,
|
183
|
+
'master' => master,
|
184
|
+
'replicas' => replicas
|
185
|
+
}
|
186
|
+
end
|
187
|
+
}
|
188
|
+
|
189
|
+
HashifyClusterNodes = lambda { |reply|
|
190
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
191
|
+
}
|
192
|
+
|
193
|
+
HashifyClusterSlaves = lambda { |reply|
|
194
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
195
|
+
}
|
196
|
+
|
197
|
+
Noop = ->(reply) { reply }
|
198
|
+
|
199
|
+
# Sends a command to Redis and returns its reply.
|
200
|
+
#
|
201
|
+
# Replies are converted to Ruby objects according to the RESP protocol, so
|
202
|
+
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
|
203
|
+
# level transformations, such as converting an array of pairs into a Ruby
|
204
|
+
# hash, are up to consumers.
|
205
|
+
#
|
206
|
+
# Redis error replies are raised as Ruby exceptions.
|
207
|
+
def call(*command)
|
208
|
+
send_command(command)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Interact with the sentinel command (masters, master, slaves, failover)
|
212
|
+
#
|
213
|
+
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
214
|
+
# @param [Array<String>] args depends on subcommand
|
215
|
+
# @return [Array<String>, Hash<String, String>, String] depends on subcommand
|
216
|
+
def sentinel(subcommand, *args)
|
217
|
+
subcommand = subcommand.to_s.downcase
|
218
|
+
send_command([:sentinel, subcommand] + args) do |reply|
|
219
|
+
case subcommand
|
220
|
+
when "get-master-addr-by-name"
|
221
|
+
reply
|
222
|
+
else
|
223
|
+
if reply.is_a?(Array)
|
224
|
+
if reply[0].is_a?(Array)
|
225
|
+
reply.map(&Hashify)
|
226
|
+
else
|
227
|
+
Hashify.call(reply)
|
228
|
+
end
|
229
|
+
else
|
230
|
+
reply
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def method_missing(*command) # rubocop:disable Style/MissingRespondToMissing
|
239
|
+
send_command(command)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require "redis/connection/registry"
|
4
|
+
require "redis/connection/command_helper"
|
5
|
+
require "redis/errors"
|
6
|
+
|
6
7
|
require "socket"
|
7
8
|
require "timeout"
|
8
9
|
|
@@ -133,7 +134,9 @@ class Redis
|
|
133
134
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
134
135
|
# Use the blocking #readpartial method instead.
|
135
136
|
|
136
|
-
def _read_from_socket(nbytes)
|
137
|
+
def _read_from_socket(nbytes, _buffer = nil)
|
138
|
+
# JRuby: Throw away the buffer as we won't need it
|
139
|
+
# but still need to support the max arity of 2
|
137
140
|
readpartial(nbytes)
|
138
141
|
rescue EOFError
|
139
142
|
raise Errno::ECONNRESET
|
@@ -240,7 +243,7 @@ class Redis
|
|
240
243
|
end
|
241
244
|
|
242
245
|
def self.connect(host, port, timeout, ssl_params)
|
243
|
-
#
|
246
|
+
# NOTE: this is using Redis::Connection::TCPSocket
|
244
247
|
tcp_sock = TCPSocket.connect(host, port, timeout)
|
245
248
|
|
246
249
|
ctx = OpenSSL::SSL::SSLContext.new
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require "redis/connection/registry"
|
4
|
+
require "redis/connection/command_helper"
|
5
|
+
require "redis/errors"
|
6
|
+
|
6
7
|
require "em-synchrony"
|
7
8
|
require "hiredis/reader"
|
8
9
|
|
9
|
-
|
10
|
-
"The redis synchrony driver is deprecated and will be removed in redis-rb 5.0. " \
|
10
|
+
::Redis.deprecate!(
|
11
|
+
"The redis synchrony driver is deprecated and will be removed in redis-rb 5.0.0. " \
|
11
12
|
"We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
|
12
13
|
)
|
13
14
|
|
@@ -129,11 +130,12 @@ class Redis
|
|
129
130
|
def read
|
130
131
|
type, payload = @connection.read
|
131
132
|
|
132
|
-
|
133
|
+
case type
|
134
|
+
when :reply
|
133
135
|
payload
|
134
|
-
|
136
|
+
when :error
|
135
137
|
raise payload
|
136
|
-
|
138
|
+
when :timeout
|
137
139
|
raise TimeoutError
|
138
140
|
else
|
139
141
|
raise "Unknown type #{type.inspect}"
|
data/lib/redis/connection.rb
CHANGED
data/lib/redis/distributed.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "redis/hash_ring"
|
4
4
|
|
5
5
|
class Redis
|
6
6
|
class Distributed
|
@@ -178,15 +178,11 @@ class Redis
|
|
178
178
|
# Determine if a key exists.
|
179
179
|
def exists(*args)
|
180
180
|
if !Redis.exists_returns_integer && args.size == 1
|
181
|
-
|
181
|
+
::Redis.deprecate!(
|
182
|
+
"`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
|
182
183
|
"use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
|
183
184
|
"(#{::Kernel.caller(1, 1).first})\n"
|
184
|
-
|
185
|
-
if defined?(::Warning)
|
186
|
-
::Warning.warn(message)
|
187
|
-
else
|
188
|
-
warn(message)
|
189
|
-
end
|
185
|
+
)
|
190
186
|
exists?(*args)
|
191
187
|
else
|
192
188
|
keys_per_node = args.group_by { |key| node_for(key) }
|
@@ -215,6 +211,13 @@ class Redis
|
|
215
211
|
node_for(key).move(key, db)
|
216
212
|
end
|
217
213
|
|
214
|
+
# Copy a value from one key to another.
|
215
|
+
def copy(source, destination, **options)
|
216
|
+
ensure_same_node(:copy, [source, destination]) do |node|
|
217
|
+
node.copy(source, destination, **options)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
218
221
|
# Return a random key from the keyspace.
|
219
222
|
def randomkey
|
220
223
|
raise CannotDistribute, :randomkey
|
@@ -666,11 +669,19 @@ class Redis
|
|
666
669
|
node_for(key).zmscore(key, *members)
|
667
670
|
end
|
668
671
|
|
669
|
-
# Return a range of members in a sorted set, by index.
|
672
|
+
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
|
670
673
|
def zrange(key, start, stop, **options)
|
671
674
|
node_for(key).zrange(key, start, stop, **options)
|
672
675
|
end
|
673
676
|
|
677
|
+
# Select a range of members in a sorted set, by index, score or lexicographical ordering
|
678
|
+
# and store the resulting sorted set in a new key.
|
679
|
+
def zrangestore(dest_key, src_key, start, stop, **options)
|
680
|
+
ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
|
681
|
+
node.zrangestore(dest_key, src_key, start, stop, **options)
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
674
685
|
# Return a range of members in a sorted set, by index, with scores ordered
|
675
686
|
# from high to low.
|
676
687
|
def zrevrange(key, start, stop, **options)
|
@@ -729,6 +740,13 @@ class Redis
|
|
729
740
|
end
|
730
741
|
end
|
731
742
|
|
743
|
+
# Return the union of multiple sorted sets.
|
744
|
+
def zunion(*keys, **options)
|
745
|
+
ensure_same_node(:zunion, keys) do |node|
|
746
|
+
node.zunion(*keys, **options)
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
732
750
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
733
751
|
def zunionstore(destination, keys, **options)
|
734
752
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
@@ -736,6 +754,21 @@ class Redis
|
|
736
754
|
end
|
737
755
|
end
|
738
756
|
|
757
|
+
# Return the difference between the first and all successive input sorted sets.
|
758
|
+
def zdiff(*keys, **options)
|
759
|
+
ensure_same_node(:zdiff, keys) do |node|
|
760
|
+
node.zdiff(*keys, **options)
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
# Compute the difference between the first and all successive input sorted sets
|
765
|
+
# and store the resulting sorted set in a new key.
|
766
|
+
def zdiffstore(destination, keys, **options)
|
767
|
+
ensure_same_node(:zdiffstore, [destination] + keys) do |node|
|
768
|
+
node.zdiffstore(destination, keys, **options)
|
769
|
+
end
|
770
|
+
end
|
771
|
+
|
739
772
|
# Get the number of fields in a hash.
|
740
773
|
def hlen(key)
|
741
774
|
node_for(key).hlen(key)
|
@@ -774,6 +807,10 @@ class Redis
|
|
774
807
|
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
775
808
|
end
|
776
809
|
|
810
|
+
def hrandfield(key, count = nil, **options)
|
811
|
+
node_for(key).hrandfield(key, count, **options)
|
812
|
+
end
|
813
|
+
|
777
814
|
# Delete one or more hash fields.
|
778
815
|
def hdel(key, *fields)
|
779
816
|
node_for(key).hdel(key, *fields)
|
data/lib/redis/pipeline.rb
CHANGED
@@ -1,7 +1,70 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "delegate"
|
4
|
+
|
3
5
|
class Redis
|
6
|
+
class PipelinedConnection
|
7
|
+
def initialize(pipeline)
|
8
|
+
@pipeline = pipeline
|
9
|
+
end
|
10
|
+
|
11
|
+
include Commands
|
12
|
+
|
13
|
+
def db
|
14
|
+
@pipeline.db
|
15
|
+
end
|
16
|
+
|
17
|
+
def db=(db)
|
18
|
+
@pipeline.db = db
|
19
|
+
end
|
20
|
+
|
21
|
+
def pipelined
|
22
|
+
yield self
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def synchronize
|
28
|
+
yield self
|
29
|
+
end
|
30
|
+
|
31
|
+
def send_command(command, &block)
|
32
|
+
@pipeline.call(command, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def send_blocking_command(command, timeout, &block)
|
36
|
+
@pipeline.call_with_timeout(command, timeout, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
4
40
|
class Pipeline
|
41
|
+
REDIS_INTERNAL_PATH = File.expand_path("..", __dir__).freeze
|
42
|
+
# Redis use MonitorMixin#synchronize and this class use DelegateClass which we want to filter out.
|
43
|
+
# Both are in the stdlib so we can simply filter the entire stdlib out.
|
44
|
+
STDLIB_PATH = File.expand_path("..", MonitorMixin.instance_method(:synchronize).source_location.first).freeze
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def deprecation_warning(method, caller_locations) # :nodoc:
|
48
|
+
callsite = caller_locations.find { |l| !l.path.start_with?(REDIS_INTERNAL_PATH, STDLIB_PATH) }
|
49
|
+
callsite ||= caller_locations.last # The caller_locations should be large enough, but just in case.
|
50
|
+
::Redis.deprecate! <<~MESSAGE
|
51
|
+
Pipelining commands on a Redis instance is deprecated and will be removed in Redis 5.0.0.
|
52
|
+
|
53
|
+
redis.#{method} do
|
54
|
+
redis.get("key")
|
55
|
+
end
|
56
|
+
|
57
|
+
should be replaced by
|
58
|
+
|
59
|
+
redis.#{method} do |pipeline|
|
60
|
+
pipeline.get("key")
|
61
|
+
end
|
62
|
+
|
63
|
+
(called from #{callsite}}
|
64
|
+
MESSAGE
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
5
68
|
attr_accessor :db
|
6
69
|
attr_reader :client
|
7
70
|
|
@@ -124,6 +187,36 @@ class Redis
|
|
124
187
|
end
|
125
188
|
end
|
126
189
|
|
190
|
+
class DeprecatedPipeline < DelegateClass(Pipeline)
|
191
|
+
def initialize(pipeline)
|
192
|
+
super(pipeline)
|
193
|
+
@deprecation_displayed = false
|
194
|
+
end
|
195
|
+
|
196
|
+
def __getobj__
|
197
|
+
unless @deprecation_displayed
|
198
|
+
Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 10))
|
199
|
+
@deprecation_displayed = true
|
200
|
+
end
|
201
|
+
@delegate_dc_obj
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class DeprecatedMulti < DelegateClass(Pipeline::Multi)
|
206
|
+
def initialize(pipeline)
|
207
|
+
super(pipeline)
|
208
|
+
@deprecation_displayed = false
|
209
|
+
end
|
210
|
+
|
211
|
+
def __getobj__
|
212
|
+
unless @deprecation_displayed
|
213
|
+
Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 10))
|
214
|
+
@deprecation_displayed = true
|
215
|
+
end
|
216
|
+
@delegate_dc_obj
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
127
220
|
class FutureNotReady < RuntimeError
|
128
221
|
def initialize
|
129
222
|
super("Value will be available once the pipeline executes.")
|
@@ -143,11 +236,11 @@ class Redis
|
|
143
236
|
end
|
144
237
|
|
145
238
|
def ==(_other)
|
146
|
-
message = +"The methods == and != are deprecated for Redis::Future and will be removed in
|
239
|
+
message = +"The methods == and != are deprecated for Redis::Future and will be removed in 5.0.0"
|
147
240
|
message << " - You probably meant to call .value == or .value !="
|
148
241
|
message << " (#{::Kernel.caller(1, 1).first})\n"
|
149
242
|
|
150
|
-
::
|
243
|
+
::Redis.deprecate!(message)
|
151
244
|
|
152
245
|
super
|
153
246
|
end
|
data/lib/redis/version.rb
CHANGED