redis 3.3.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 +5 -5
- data/CHANGELOG.md +290 -2
- data/README.md +146 -146
- data/lib/redis/client.rb +79 -541
- 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 +328 -108
- data/lib/redis/errors.rb +23 -1
- data/lib/redis/hash_ring.rb +36 -79
- data/lib/redis/pipeline.rb +69 -83
- data/lib/redis/subscribe.rb +26 -19
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +115 -2695
- metadata +38 -218
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/lib/redis/connection/command_helper.rb +0 -44
- data/lib/redis/connection/hiredis.rb +0 -66
- data/lib/redis/connection/registry.rb +0 -12
- data/lib/redis/connection/ruby.rb +0 -429
- data/lib/redis/connection/synchrony.rb +0 -133
- data/lib/redis/connection.rb +0 -9
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
@@ -1,429 +0,0 @@
|
|
1
|
-
require "redis/connection/registry"
|
2
|
-
require "redis/connection/command_helper"
|
3
|
-
require "redis/errors"
|
4
|
-
require "socket"
|
5
|
-
require "timeout"
|
6
|
-
|
7
|
-
begin
|
8
|
-
require "openssl"
|
9
|
-
rescue LoadError
|
10
|
-
# Not all systems have OpenSSL support
|
11
|
-
end
|
12
|
-
|
13
|
-
if RUBY_VERSION < "1.9.3"
|
14
|
-
class String
|
15
|
-
# Ruby 1.8.7 does not have byteslice, but it handles encodings differently anyway.
|
16
|
-
# We can simply slice the string, which is a byte array there.
|
17
|
-
def byteslice(*args)
|
18
|
-
slice(*args)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Redis
|
24
|
-
module Connection
|
25
|
-
module SocketMixin
|
26
|
-
|
27
|
-
CRLF = "\r\n".freeze
|
28
|
-
|
29
|
-
# Exceptions raised during non-blocking I/O ops that require retrying the op
|
30
|
-
if RUBY_VERSION >= "1.9.3"
|
31
|
-
NBIO_READ_EXCEPTIONS = [IO::WaitReadable]
|
32
|
-
NBIO_WRITE_EXCEPTIONS = [IO::WaitWritable]
|
33
|
-
else
|
34
|
-
NBIO_READ_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
|
35
|
-
NBIO_WRITE_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
|
36
|
-
end
|
37
|
-
|
38
|
-
def initialize(*args)
|
39
|
-
super(*args)
|
40
|
-
|
41
|
-
@timeout = @write_timeout = nil
|
42
|
-
@buffer = ""
|
43
|
-
end
|
44
|
-
|
45
|
-
def timeout=(timeout)
|
46
|
-
if timeout && timeout > 0
|
47
|
-
@timeout = timeout
|
48
|
-
else
|
49
|
-
@timeout = nil
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def write_timeout=(timeout)
|
54
|
-
if timeout && timeout > 0
|
55
|
-
@write_timeout = timeout
|
56
|
-
else
|
57
|
-
@write_timeout = nil
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def read(nbytes)
|
62
|
-
result = @buffer.slice!(0, nbytes)
|
63
|
-
|
64
|
-
while result.bytesize < nbytes
|
65
|
-
result << _read_from_socket(nbytes - result.bytesize)
|
66
|
-
end
|
67
|
-
|
68
|
-
result
|
69
|
-
end
|
70
|
-
|
71
|
-
def gets
|
72
|
-
crlf = nil
|
73
|
-
|
74
|
-
while (crlf = @buffer.index(CRLF)) == nil
|
75
|
-
@buffer << _read_from_socket(1024)
|
76
|
-
end
|
77
|
-
|
78
|
-
@buffer.slice!(0, crlf + CRLF.bytesize)
|
79
|
-
end
|
80
|
-
|
81
|
-
def _read_from_socket(nbytes)
|
82
|
-
|
83
|
-
begin
|
84
|
-
read_nonblock(nbytes)
|
85
|
-
|
86
|
-
rescue *NBIO_READ_EXCEPTIONS
|
87
|
-
if IO.select([self], nil, nil, @timeout)
|
88
|
-
retry
|
89
|
-
else
|
90
|
-
raise Redis::TimeoutError
|
91
|
-
end
|
92
|
-
rescue *NBIO_WRITE_EXCEPTIONS
|
93
|
-
if IO.select(nil, [self], nil, @timeout)
|
94
|
-
retry
|
95
|
-
else
|
96
|
-
raise Redis::TimeoutError
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
rescue EOFError
|
101
|
-
raise Errno::ECONNRESET
|
102
|
-
end
|
103
|
-
|
104
|
-
def _write_to_socket(data)
|
105
|
-
begin
|
106
|
-
write_nonblock(data)
|
107
|
-
|
108
|
-
rescue *NBIO_WRITE_EXCEPTIONS
|
109
|
-
if IO.select(nil, [self], nil, @write_timeout)
|
110
|
-
retry
|
111
|
-
else
|
112
|
-
raise Redis::TimeoutError
|
113
|
-
end
|
114
|
-
rescue *NBIO_READ_EXCEPTIONS
|
115
|
-
if IO.select([self], nil, nil, @write_timeout)
|
116
|
-
retry
|
117
|
-
else
|
118
|
-
raise Redis::TimeoutError
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
rescue EOFError
|
123
|
-
raise Errno::ECONNRESET
|
124
|
-
end
|
125
|
-
|
126
|
-
def write(data)
|
127
|
-
return super(data) unless @write_timeout
|
128
|
-
|
129
|
-
length = data.bytesize
|
130
|
-
total_count = 0
|
131
|
-
loop do
|
132
|
-
count = _write_to_socket(data)
|
133
|
-
|
134
|
-
total_count += count
|
135
|
-
return total_count if total_count >= length
|
136
|
-
data = data.byteslice(count..-1)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
142
|
-
|
143
|
-
require "timeout"
|
144
|
-
|
145
|
-
class TCPSocket < ::TCPSocket
|
146
|
-
|
147
|
-
include SocketMixin
|
148
|
-
|
149
|
-
def self.connect(host, port, timeout)
|
150
|
-
Timeout.timeout(timeout) do
|
151
|
-
sock = new(host, port)
|
152
|
-
sock
|
153
|
-
end
|
154
|
-
rescue Timeout::Error
|
155
|
-
raise TimeoutError
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
if defined?(::UNIXSocket)
|
160
|
-
|
161
|
-
class UNIXSocket < ::UNIXSocket
|
162
|
-
|
163
|
-
include SocketMixin
|
164
|
-
|
165
|
-
def self.connect(path, timeout)
|
166
|
-
Timeout.timeout(timeout) do
|
167
|
-
sock = new(path)
|
168
|
-
sock
|
169
|
-
end
|
170
|
-
rescue Timeout::Error
|
171
|
-
raise TimeoutError
|
172
|
-
end
|
173
|
-
|
174
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when IO.select
|
175
|
-
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
176
|
-
# Use the blocking #readpartial method instead.
|
177
|
-
|
178
|
-
def _read_from_socket(nbytes)
|
179
|
-
readpartial(nbytes)
|
180
|
-
|
181
|
-
rescue EOFError
|
182
|
-
raise Errno::ECONNRESET
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
end
|
187
|
-
|
188
|
-
else
|
189
|
-
|
190
|
-
class TCPSocket < ::Socket
|
191
|
-
|
192
|
-
include SocketMixin
|
193
|
-
|
194
|
-
def self.connect_addrinfo(ai, port, timeout)
|
195
|
-
sock = new(::Socket.const_get(ai[0]), Socket::SOCK_STREAM, 0)
|
196
|
-
sockaddr = ::Socket.pack_sockaddr_in(port, ai[3])
|
197
|
-
|
198
|
-
begin
|
199
|
-
sock.connect_nonblock(sockaddr)
|
200
|
-
rescue Errno::EINPROGRESS
|
201
|
-
if IO.select(nil, [sock], nil, timeout) == nil
|
202
|
-
raise TimeoutError
|
203
|
-
end
|
204
|
-
|
205
|
-
begin
|
206
|
-
sock.connect_nonblock(sockaddr)
|
207
|
-
rescue Errno::EISCONN
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
sock
|
212
|
-
end
|
213
|
-
|
214
|
-
def self.connect(host, port, timeout)
|
215
|
-
# Don't pass AI_ADDRCONFIG as flag to getaddrinfo(3)
|
216
|
-
#
|
217
|
-
# From the man page for getaddrinfo(3):
|
218
|
-
#
|
219
|
-
# If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4
|
220
|
-
# addresses are returned in the list pointed to by res only if the
|
221
|
-
# local system has at least one IPv4 address configured, and IPv6
|
222
|
-
# addresses are returned only if the local system has at least one
|
223
|
-
# IPv6 address configured. The loopback address is not considered
|
224
|
-
# for this case as valid as a configured address.
|
225
|
-
#
|
226
|
-
# We do want the IPv6 loopback address to be returned if applicable,
|
227
|
-
# even if it is the only configured IPv6 address on the machine.
|
228
|
-
# Also see: https://github.com/redis/redis-rb/pull/394.
|
229
|
-
addrinfo = ::Socket.getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
|
230
|
-
|
231
|
-
# From the man page for getaddrinfo(3):
|
232
|
-
#
|
233
|
-
# Normally, the application should try using the addresses in the
|
234
|
-
# order in which they are returned. The sorting function used
|
235
|
-
# within getaddrinfo() is defined in RFC 3484 [...].
|
236
|
-
#
|
237
|
-
addrinfo.each_with_index do |ai, i|
|
238
|
-
begin
|
239
|
-
return connect_addrinfo(ai, port, timeout)
|
240
|
-
rescue SystemCallError
|
241
|
-
# Raise if this was our last attempt.
|
242
|
-
raise if addrinfo.length == i+1
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
class UNIXSocket < ::Socket
|
249
|
-
|
250
|
-
include SocketMixin
|
251
|
-
|
252
|
-
def self.connect(path, timeout)
|
253
|
-
sock = new(::Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
|
254
|
-
sockaddr = ::Socket.pack_sockaddr_un(path)
|
255
|
-
|
256
|
-
begin
|
257
|
-
sock.connect_nonblock(sockaddr)
|
258
|
-
rescue Errno::EINPROGRESS
|
259
|
-
if IO.select(nil, [sock], nil, timeout) == nil
|
260
|
-
raise TimeoutError
|
261
|
-
end
|
262
|
-
|
263
|
-
begin
|
264
|
-
sock.connect_nonblock(sockaddr)
|
265
|
-
rescue Errno::EISCONN
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
sock
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
end
|
274
|
-
|
275
|
-
if defined?(OpenSSL)
|
276
|
-
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
277
|
-
include SocketMixin
|
278
|
-
|
279
|
-
def self.connect(host, port, timeout, ssl_params)
|
280
|
-
# Note: this is using Redis::Connection::TCPSocket
|
281
|
-
tcp_sock = TCPSocket.connect(host, port, timeout)
|
282
|
-
|
283
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
284
|
-
ctx.set_params(ssl_params) if ssl_params && !ssl_params.empty?
|
285
|
-
|
286
|
-
ssl_sock = new(tcp_sock, ctx)
|
287
|
-
ssl_sock.hostname = host
|
288
|
-
ssl_sock.connect
|
289
|
-
ssl_sock.post_connection_check(host)
|
290
|
-
|
291
|
-
ssl_sock
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
class Ruby
|
297
|
-
include Redis::Connection::CommandHelper
|
298
|
-
|
299
|
-
MINUS = "-".freeze
|
300
|
-
PLUS = "+".freeze
|
301
|
-
COLON = ":".freeze
|
302
|
-
DOLLAR = "$".freeze
|
303
|
-
ASTERISK = "*".freeze
|
304
|
-
|
305
|
-
def self.connect(config)
|
306
|
-
if config[:scheme] == "unix"
|
307
|
-
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
308
|
-
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
309
|
-
elsif config[:scheme] == "rediss" || config[:ssl]
|
310
|
-
raise ArgumentError, "This library does not support SSL on Ruby < 1.9" if RUBY_VERSION < "1.9.3"
|
311
|
-
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
312
|
-
else
|
313
|
-
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
314
|
-
end
|
315
|
-
|
316
|
-
instance = new(sock)
|
317
|
-
instance.timeout = config[:timeout]
|
318
|
-
instance.write_timeout = config[:write_timeout]
|
319
|
-
instance.set_tcp_keepalive config[:tcp_keepalive]
|
320
|
-
instance
|
321
|
-
end
|
322
|
-
|
323
|
-
if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c}
|
324
|
-
def set_tcp_keepalive(keepalive)
|
325
|
-
return unless keepalive.is_a?(Hash)
|
326
|
-
|
327
|
-
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
328
|
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, keepalive[:time])
|
329
|
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, keepalive[:intvl])
|
330
|
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, keepalive[:probes])
|
331
|
-
end
|
332
|
-
|
333
|
-
def get_tcp_keepalive
|
334
|
-
{
|
335
|
-
:time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
|
336
|
-
:intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
|
337
|
-
:probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int,
|
338
|
-
}
|
339
|
-
end
|
340
|
-
else
|
341
|
-
def set_tcp_keepalive(keepalive)
|
342
|
-
end
|
343
|
-
|
344
|
-
def get_tcp_keepalive
|
345
|
-
{
|
346
|
-
}
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
def initialize(sock)
|
351
|
-
@sock = sock
|
352
|
-
end
|
353
|
-
|
354
|
-
def connected?
|
355
|
-
!! @sock
|
356
|
-
end
|
357
|
-
|
358
|
-
def disconnect
|
359
|
-
@sock.close
|
360
|
-
rescue
|
361
|
-
ensure
|
362
|
-
@sock = nil
|
363
|
-
end
|
364
|
-
|
365
|
-
def timeout=(timeout)
|
366
|
-
if @sock.respond_to?(:timeout=)
|
367
|
-
@sock.timeout = timeout
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
def write_timeout=(timeout)
|
372
|
-
@sock.write_timeout = timeout
|
373
|
-
end
|
374
|
-
|
375
|
-
def write(command)
|
376
|
-
@sock.write(build_command(command))
|
377
|
-
end
|
378
|
-
|
379
|
-
def read
|
380
|
-
line = @sock.gets
|
381
|
-
reply_type = line.slice!(0, 1)
|
382
|
-
format_reply(reply_type, line)
|
383
|
-
|
384
|
-
rescue Errno::EAGAIN
|
385
|
-
raise TimeoutError
|
386
|
-
end
|
387
|
-
|
388
|
-
def format_reply(reply_type, line)
|
389
|
-
case reply_type
|
390
|
-
when MINUS then format_error_reply(line)
|
391
|
-
when PLUS then format_status_reply(line)
|
392
|
-
when COLON then format_integer_reply(line)
|
393
|
-
when DOLLAR then format_bulk_reply(line)
|
394
|
-
when ASTERISK then format_multi_bulk_reply(line)
|
395
|
-
else raise ProtocolError.new(reply_type)
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
|
-
def format_error_reply(line)
|
400
|
-
CommandError.new(line.strip)
|
401
|
-
end
|
402
|
-
|
403
|
-
def format_status_reply(line)
|
404
|
-
line.strip
|
405
|
-
end
|
406
|
-
|
407
|
-
def format_integer_reply(line)
|
408
|
-
line.to_i
|
409
|
-
end
|
410
|
-
|
411
|
-
def format_bulk_reply(line)
|
412
|
-
bulklen = line.to_i
|
413
|
-
return if bulklen == -1
|
414
|
-
reply = encode(@sock.read(bulklen))
|
415
|
-
@sock.read(2) # Discard CRLF.
|
416
|
-
reply
|
417
|
-
end
|
418
|
-
|
419
|
-
def format_multi_bulk_reply(line)
|
420
|
-
n = line.to_i
|
421
|
-
return if n == -1
|
422
|
-
|
423
|
-
Array.new(n) { read }
|
424
|
-
end
|
425
|
-
end
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
Redis::Connection.drivers << Redis::Connection::Ruby
|
@@ -1,133 +0,0 @@
|
|
1
|
-
require "redis/connection/command_helper"
|
2
|
-
require "redis/connection/registry"
|
3
|
-
require "redis/errors"
|
4
|
-
require "em-synchrony"
|
5
|
-
require "hiredis/reader"
|
6
|
-
|
7
|
-
class Redis
|
8
|
-
module Connection
|
9
|
-
class RedisClient < EventMachine::Connection
|
10
|
-
include EventMachine::Deferrable
|
11
|
-
|
12
|
-
attr_accessor :timeout
|
13
|
-
|
14
|
-
def post_init
|
15
|
-
@req = nil
|
16
|
-
@connected = false
|
17
|
-
@reader = ::Hiredis::Reader.new
|
18
|
-
end
|
19
|
-
|
20
|
-
def connection_completed
|
21
|
-
@connected = true
|
22
|
-
succeed
|
23
|
-
end
|
24
|
-
|
25
|
-
def connected?
|
26
|
-
@connected
|
27
|
-
end
|
28
|
-
|
29
|
-
def receive_data(data)
|
30
|
-
@reader.feed(data)
|
31
|
-
|
32
|
-
loop do
|
33
|
-
begin
|
34
|
-
reply = @reader.gets
|
35
|
-
rescue RuntimeError => err
|
36
|
-
@req.fail [:error, ProtocolError.new(err.message)]
|
37
|
-
break
|
38
|
-
end
|
39
|
-
|
40
|
-
break if reply == false
|
41
|
-
|
42
|
-
reply = CommandError.new(reply.message) if reply.is_a?(RuntimeError)
|
43
|
-
@req.succeed [:reply, reply]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def read
|
48
|
-
@req = EventMachine::DefaultDeferrable.new
|
49
|
-
if @timeout > 0
|
50
|
-
@req.timeout(@timeout, :timeout)
|
51
|
-
end
|
52
|
-
EventMachine::Synchrony.sync @req
|
53
|
-
end
|
54
|
-
|
55
|
-
def send(data)
|
56
|
-
callback { send_data data }
|
57
|
-
end
|
58
|
-
|
59
|
-
def unbind
|
60
|
-
@connected = false
|
61
|
-
if @req
|
62
|
-
@req.fail [:error, Errno::ECONNRESET]
|
63
|
-
@req = nil
|
64
|
-
else
|
65
|
-
fail
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class Synchrony
|
71
|
-
include Redis::Connection::CommandHelper
|
72
|
-
|
73
|
-
def self.connect(config)
|
74
|
-
if config[:scheme] == "unix"
|
75
|
-
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
76
|
-
elsif config[:scheme] == "rediss" || config[:ssl]
|
77
|
-
raise NotImplementedError, "SSL not supported by synchrony driver"
|
78
|
-
else
|
79
|
-
conn = EventMachine.connect(config[:host], config[:port], RedisClient) do |c|
|
80
|
-
c.pending_connect_timeout = [config[:connect_timeout], 0.1].max
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
fiber = Fiber.current
|
85
|
-
conn.callback { fiber.resume }
|
86
|
-
conn.errback { fiber.resume :refused }
|
87
|
-
|
88
|
-
raise Errno::ECONNREFUSED if Fiber.yield == :refused
|
89
|
-
|
90
|
-
instance = new(conn)
|
91
|
-
instance.timeout = config[:read_timeout]
|
92
|
-
instance
|
93
|
-
end
|
94
|
-
|
95
|
-
def initialize(connection)
|
96
|
-
@connection = connection
|
97
|
-
end
|
98
|
-
|
99
|
-
def connected?
|
100
|
-
@connection && @connection.connected?
|
101
|
-
end
|
102
|
-
|
103
|
-
def timeout=(timeout)
|
104
|
-
@connection.timeout = timeout
|
105
|
-
end
|
106
|
-
|
107
|
-
def disconnect
|
108
|
-
@connection.close_connection
|
109
|
-
@connection = nil
|
110
|
-
end
|
111
|
-
|
112
|
-
def write(command)
|
113
|
-
@connection.send(build_command(command))
|
114
|
-
end
|
115
|
-
|
116
|
-
def read
|
117
|
-
type, payload = @connection.read
|
118
|
-
|
119
|
-
if type == :reply
|
120
|
-
payload
|
121
|
-
elsif type == :error
|
122
|
-
raise payload
|
123
|
-
elsif type == :timeout
|
124
|
-
raise TimeoutError
|
125
|
-
else
|
126
|
-
raise "Unknown type #{type.inspect}"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
Redis::Connection.drivers << Redis::Connection::Synchrony
|
data/lib/redis/connection.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
require "redis/connection/registry"
|
2
|
-
|
3
|
-
# If a connection driver was required before this file, the array
|
4
|
-
# Redis::Connection.drivers will contain one or more classes. The last driver
|
5
|
-
# in this array will be used as default driver. If this array is empty, we load
|
6
|
-
# the plain Ruby driver as our default. Another driver can be required at a
|
7
|
-
# later point in time, causing it to be the last element of the #drivers array
|
8
|
-
# and therefore be chosen by default.
|
9
|
-
require "redis/connection/ruby" if Redis::Connection.drivers.empty?
|
data/redis.gemspec
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
$:.unshift File.expand_path("../lib", __FILE__)
|
4
|
-
|
5
|
-
require "redis/version"
|
6
|
-
|
7
|
-
Gem::Specification.new do |s|
|
8
|
-
s.name = "redis"
|
9
|
-
|
10
|
-
s.version = Redis::VERSION
|
11
|
-
|
12
|
-
s.homepage = "https://github.com/redis/redis-rb"
|
13
|
-
|
14
|
-
s.summary = "A Ruby client library for Redis"
|
15
|
-
|
16
|
-
s.description = <<-EOS
|
17
|
-
A Ruby client that tries to match Redis' API one-to-one, while still
|
18
|
-
providing an idiomatic interface. It features thread-safety,
|
19
|
-
client-side sharding, pipelining, and an obsession for performance.
|
20
|
-
EOS
|
21
|
-
|
22
|
-
s.license = "MIT"
|
23
|
-
|
24
|
-
s.authors = [
|
25
|
-
"Ezra Zygmuntowicz",
|
26
|
-
"Taylor Weibley",
|
27
|
-
"Matthew Clark",
|
28
|
-
"Brian McKinney",
|
29
|
-
"Salvatore Sanfilippo",
|
30
|
-
"Luca Guidi",
|
31
|
-
"Michel Martens",
|
32
|
-
"Damian Janowski",
|
33
|
-
"Pieter Noordhuis"
|
34
|
-
]
|
35
|
-
|
36
|
-
s.email = ["redis-db@googlegroups.com"]
|
37
|
-
|
38
|
-
s.files = `git ls-files`.split("\n")
|
39
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
40
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
41
|
-
|
42
|
-
s.add_development_dependency("rake", "<11.0.0")
|
43
|
-
s.add_development_dependency("test-unit", "3.1.5")
|
44
|
-
end
|
data/test/bitpos_test.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
unless defined?(Enumerator)
|
6
|
-
Enumerator = Enumerable::Enumerator
|
7
|
-
end
|
8
|
-
|
9
|
-
class TestBitpos < Test::Unit::TestCase
|
10
|
-
|
11
|
-
include Helper::Client
|
12
|
-
|
13
|
-
def test_bitpos_empty_zero
|
14
|
-
target_version "2.9.11" do
|
15
|
-
r.del "foo"
|
16
|
-
assert_equal 0, r.bitpos("foo", 0)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_bitpos_empty_one
|
21
|
-
target_version "2.9.11" do
|
22
|
-
r.del "foo"
|
23
|
-
assert_equal -1, r.bitpos("foo", 1)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_bitpos_zero
|
28
|
-
target_version "2.9.11" do
|
29
|
-
r.set "foo", "\xff\xf0\x00"
|
30
|
-
assert_equal 12, r.bitpos("foo", 0)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_bitpos_one
|
35
|
-
target_version "2.9.11" do
|
36
|
-
r.set "foo", "\x00\x0f\x00"
|
37
|
-
assert_equal 12, r.bitpos("foo", 1)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_bitpos_zero_end_is_given
|
42
|
-
target_version "2.9.11" do
|
43
|
-
r.set "foo", "\xff\xff\xff"
|
44
|
-
assert_equal 24, r.bitpos("foo", 0)
|
45
|
-
assert_equal 24, r.bitpos("foo", 0, 0)
|
46
|
-
assert_equal -1, r.bitpos("foo", 0, 0, -1)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_bitpos_one_intervals
|
51
|
-
target_version "2.9.11" do
|
52
|
-
r.set "foo", "\x00\xff\x00"
|
53
|
-
assert_equal 8, r.bitpos("foo", 1, 0, -1)
|
54
|
-
assert_equal 8, r.bitpos("foo", 1, 1, -1)
|
55
|
-
assert_equal -1, r.bitpos("foo", 1, 2, -1)
|
56
|
-
assert_equal -1, r.bitpos("foo", 1, 2, 200)
|
57
|
-
assert_equal 8, r.bitpos("foo", 1, 1, 1)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_bitpos_raise_exception_if_stop_not_start
|
62
|
-
target_version "2.9.11" do
|
63
|
-
assert_raises(ArgumentError) do
|
64
|
-
r.bitpos("foo", 0, nil, 2)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|