redis 3.0.0.rc1 → 3.0.0.rc2
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.
- data/.travis.yml +50 -0
- data/.travis/Gemfile +11 -0
- data/CHANGELOG.md +47 -19
- data/README.md +160 -149
- data/Rakefile +15 -50
- data/examples/pubsub.rb +1 -1
- data/examples/unicorn/config.ru +1 -1
- data/examples/unicorn/unicorn.rb +1 -1
- data/lib/redis.rb +790 -390
- data/lib/redis/client.rb +137 -49
- data/lib/redis/connection/hiredis.rb +26 -15
- data/lib/redis/connection/ruby.rb +170 -53
- data/lib/redis/connection/synchrony.rb +23 -35
- data/lib/redis/distributed.rb +92 -32
- data/lib/redis/errors.rb +4 -2
- data/lib/redis/pipeline.rb +17 -6
- data/lib/redis/version.rb +1 -1
- data/redis.gemspec +4 -6
- data/test/blocking_commands_test.rb +42 -0
- data/test/command_map_test.rb +18 -17
- data/test/commands_on_hashes_test.rb +13 -12
- data/test/commands_on_lists_test.rb +35 -45
- data/test/commands_on_sets_test.rb +55 -54
- data/test/commands_on_sorted_sets_test.rb +106 -105
- data/test/commands_on_strings_test.rb +64 -55
- data/test/commands_on_value_types_test.rb +66 -54
- data/test/connection_handling_test.rb +136 -151
- data/test/distributed_blocking_commands_test.rb +33 -40
- data/test/distributed_commands_on_hashes_test.rb +6 -7
- data/test/distributed_commands_on_lists_test.rb +13 -14
- data/test/distributed_commands_on_sets_test.rb +57 -58
- data/test/distributed_commands_on_sorted_sets_test.rb +11 -12
- data/test/distributed_commands_on_strings_test.rb +31 -32
- data/test/distributed_commands_on_value_types_test.rb +61 -46
- data/test/distributed_commands_requiring_clustering_test.rb +108 -108
- data/test/distributed_connection_handling_test.rb +14 -15
- data/test/distributed_internals_test.rb +7 -19
- data/test/distributed_key_tags_test.rb +36 -36
- data/test/distributed_persistence_control_commands_test.rb +17 -14
- data/test/distributed_publish_subscribe_test.rb +61 -69
- data/test/distributed_remote_server_control_commands_test.rb +39 -28
- data/test/distributed_sorting_test.rb +12 -13
- data/test/distributed_test.rb +40 -41
- data/test/distributed_transactions_test.rb +20 -21
- data/test/encoding_test.rb +12 -9
- data/test/error_replies_test.rb +42 -36
- data/test/helper.rb +118 -85
- data/test/helper_test.rb +20 -6
- data/test/internals_test.rb +167 -103
- data/test/lint/blocking_commands.rb +124 -0
- data/test/lint/hashes.rb +115 -93
- data/test/lint/lists.rb +86 -80
- data/test/lint/sets.rb +68 -62
- data/test/lint/sorted_sets.rb +200 -195
- data/test/lint/strings.rb +112 -94
- data/test/lint/value_types.rb +76 -55
- data/test/persistence_control_commands_test.rb +17 -12
- data/test/pipelining_commands_test.rb +135 -126
- data/test/publish_subscribe_test.rb +105 -110
- data/test/remote_server_control_commands_test.rb +74 -58
- data/test/sorting_test.rb +31 -29
- data/test/support/connection/hiredis.rb +1 -0
- data/test/support/connection/ruby.rb +1 -0
- data/test/support/connection/synchrony.rb +17 -0
- data/test/{redis_mock.rb → support/redis_mock.rb} +24 -21
- data/test/support/wire/synchrony.rb +24 -0
- data/test/support/wire/thread.rb +5 -0
- data/test/synchrony_driver.rb +9 -9
- data/test/test.conf +1 -1
- data/test/thread_safety_test.rb +21 -19
- data/test/transactions_test.rb +189 -118
- data/test/unknown_commands_test.rb +9 -8
- data/test/url_param_test.rb +46 -41
- metadata +28 -43
- data/TODO.md +0 -4
- data/benchmarking/thread_safety.rb +0 -38
- data/test/lint/internals.rb +0 -36
data/lib/redis/client.rb
CHANGED
@@ -2,40 +2,76 @@ require "redis/errors"
|
|
2
2
|
|
3
3
|
class Redis
|
4
4
|
class Client
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
DEFAULTS = {
|
7
|
+
:scheme => "redis",
|
8
|
+
:host => "127.0.0.1",
|
9
|
+
:port => 6379,
|
10
|
+
:path => nil,
|
11
|
+
:timeout => 5.0,
|
12
|
+
:password => nil,
|
13
|
+
:db => 0,
|
14
|
+
}
|
15
|
+
|
16
|
+
def scheme
|
17
|
+
@options[:scheme]
|
18
|
+
end
|
19
|
+
|
20
|
+
def host
|
21
|
+
@options[:host]
|
22
|
+
end
|
23
|
+
|
24
|
+
def port
|
25
|
+
@options[:port]
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
@options[:path]
|
30
|
+
end
|
31
|
+
|
32
|
+
def timeout
|
33
|
+
@options[:timeout]
|
34
|
+
end
|
35
|
+
|
36
|
+
def password
|
37
|
+
@options[:password]
|
38
|
+
end
|
39
|
+
|
40
|
+
def db
|
41
|
+
@options[:db]
|
42
|
+
end
|
43
|
+
|
44
|
+
def db=(db)
|
45
|
+
@options[:db] = db.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
attr :logger
|
7
49
|
attr :connection
|
8
50
|
attr :command_map
|
9
51
|
|
10
52
|
def initialize(options = {})
|
11
|
-
@
|
12
|
-
if @path.nil?
|
13
|
-
@host = options[:host] || "127.0.0.1"
|
14
|
-
@port = (options[:port] || 6379).to_i
|
15
|
-
end
|
16
|
-
|
17
|
-
@db = (options[:db] || 0).to_i
|
18
|
-
@timeout = (options[:timeout] || 5).to_f
|
19
|
-
@password = options[:password]
|
20
|
-
@logger = options[:logger]
|
53
|
+
@options = _parse_options(options)
|
21
54
|
@reconnect = true
|
22
|
-
@
|
55
|
+
@logger = @options[:logger]
|
56
|
+
@connection = nil
|
23
57
|
@command_map = {}
|
24
58
|
end
|
25
59
|
|
26
60
|
def connect
|
61
|
+
@pid = Process.pid
|
62
|
+
|
27
63
|
establish_connection
|
28
|
-
call [:auth,
|
29
|
-
call [:select,
|
64
|
+
call [:auth, password] if password
|
65
|
+
call [:select, db] if db != 0
|
30
66
|
self
|
31
67
|
end
|
32
68
|
|
33
69
|
def id
|
34
|
-
"redis://#{location}/#{db}"
|
70
|
+
@options[:id] || "redis://#{location}/#{db}"
|
35
71
|
end
|
36
72
|
|
37
73
|
def location
|
38
|
-
|
74
|
+
path || "#{host}:#{port}"
|
39
75
|
end
|
40
76
|
|
41
77
|
def call(command, &block)
|
@@ -155,11 +191,11 @@ class Redis
|
|
155
191
|
end
|
156
192
|
|
157
193
|
def connected?
|
158
|
-
connection.connected?
|
194
|
+
connection && connection.connected?
|
159
195
|
end
|
160
196
|
|
161
197
|
def disconnect
|
162
|
-
connection.disconnect if
|
198
|
+
connection.disconnect if connected?
|
163
199
|
end
|
164
200
|
|
165
201
|
def reconnect
|
@@ -169,7 +205,7 @@ class Redis
|
|
169
205
|
|
170
206
|
def io
|
171
207
|
yield
|
172
|
-
rescue
|
208
|
+
rescue TimeoutError
|
173
209
|
raise TimeoutError, "Connection timed out"
|
174
210
|
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL => e
|
175
211
|
raise ConnectionError, "Connection lost (%s)" % [e.class.name.split("::").last]
|
@@ -191,10 +227,10 @@ class Redis
|
|
191
227
|
connect unless connected?
|
192
228
|
|
193
229
|
begin
|
194
|
-
|
230
|
+
connection.timeout = 0
|
195
231
|
yield
|
196
232
|
ensure
|
197
|
-
|
233
|
+
connection.timeout = timeout if connected?
|
198
234
|
end
|
199
235
|
end
|
200
236
|
|
@@ -209,57 +245,44 @@ class Redis
|
|
209
245
|
|
210
246
|
protected
|
211
247
|
|
212
|
-
def deprecated(old, new = nil, trace = caller[0])
|
213
|
-
message = "The method #{old} is deprecated and will be removed in 2.0"
|
214
|
-
message << " - use #{new} instead" if new
|
215
|
-
Redis.deprecate(message, trace)
|
216
|
-
end
|
217
|
-
|
218
248
|
def logging(commands)
|
219
249
|
return yield unless @logger && @logger.debug?
|
220
250
|
|
221
251
|
begin
|
222
252
|
commands.each do |name, *args|
|
223
|
-
@logger.debug("Redis >> #{name.to_s.upcase} #{args.join(" ")}")
|
253
|
+
@logger.debug("Redis >> #{name.to_s.upcase} #{args.map(&:to_s).join(" ")}")
|
224
254
|
end
|
225
255
|
|
226
256
|
t1 = Time.now
|
227
257
|
yield
|
228
258
|
ensure
|
229
|
-
@logger.debug("Redis >> %0.2fms" % ((Time.now - t1) * 1000))
|
259
|
+
@logger.debug("Redis >> %0.2fms" % ((Time.now - t1) * 1000)) if t1
|
230
260
|
end
|
231
261
|
end
|
232
262
|
|
233
263
|
def establish_connection
|
234
|
-
|
235
|
-
timeout = Integer(@timeout * 1_000_000)
|
236
|
-
|
237
|
-
if @path
|
238
|
-
connection.connect_unix(@path, timeout)
|
239
|
-
else
|
240
|
-
connection.connect(@host, @port, timeout)
|
241
|
-
end
|
242
|
-
|
243
|
-
# If the timeout is set we set the low level socket options in order
|
244
|
-
# to make sure a blocking read will return after the specified number
|
245
|
-
# of seconds. This hack is from memcached ruby client.
|
246
|
-
self.timeout = @timeout
|
264
|
+
@connection = @options[:driver].connect(@options.dup)
|
247
265
|
|
248
|
-
rescue
|
266
|
+
rescue TimeoutError
|
249
267
|
raise CannotConnectError, "Timed out connecting to Redis on #{location}"
|
250
268
|
rescue Errno::ECONNREFUSED
|
251
269
|
raise CannotConnectError, "Error connecting to Redis on #{location} (ECONNREFUSED)"
|
252
270
|
end
|
253
271
|
|
254
|
-
def timeout=(timeout)
|
255
|
-
connection.timeout = Integer(timeout * 1_000_000)
|
256
|
-
end
|
257
|
-
|
258
272
|
def ensure_connected
|
259
273
|
tries = 0
|
260
274
|
|
261
275
|
begin
|
262
|
-
|
276
|
+
if connected?
|
277
|
+
if Process.pid != @pid
|
278
|
+
raise InheritedError,
|
279
|
+
"Tried to use a connection from a child process without reconnecting. " +
|
280
|
+
"You need to reconnect to Redis after forking."
|
281
|
+
end
|
282
|
+
else
|
283
|
+
connect
|
284
|
+
end
|
285
|
+
|
263
286
|
tries += 1
|
264
287
|
|
265
288
|
yield
|
@@ -276,5 +299,70 @@ class Redis
|
|
276
299
|
raise
|
277
300
|
end
|
278
301
|
end
|
302
|
+
|
303
|
+
def _parse_options(options)
|
304
|
+
defaults = DEFAULTS.dup
|
305
|
+
|
306
|
+
url = options[:url] || ENV["REDIS_URL"]
|
307
|
+
|
308
|
+
# Override defaults from URL if given
|
309
|
+
if url
|
310
|
+
require "uri"
|
311
|
+
|
312
|
+
uri = URI(url)
|
313
|
+
|
314
|
+
if uri.scheme == "unix"
|
315
|
+
defaults[:path] = uri.path
|
316
|
+
else
|
317
|
+
# Require the URL to have at least a host
|
318
|
+
raise ArgumentError, "invalid url" unless uri.host
|
319
|
+
|
320
|
+
defaults[:scheme] = uri.scheme
|
321
|
+
defaults[:host] = uri.host
|
322
|
+
defaults[:port] = uri.port if uri.port
|
323
|
+
defaults[:password] = uri.password if uri.password
|
324
|
+
defaults[:db] = uri.path[1..-1].to_i if uri.path
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
options = defaults.merge(options)
|
329
|
+
|
330
|
+
if options[:path]
|
331
|
+
options[:scheme] = "unix"
|
332
|
+
options.delete(:host)
|
333
|
+
options.delete(:port)
|
334
|
+
else
|
335
|
+
options[:host] = options[:host].to_s
|
336
|
+
options[:port] = options[:port].to_i
|
337
|
+
end
|
338
|
+
|
339
|
+
options[:timeout] = options[:timeout].to_f
|
340
|
+
options[:db] = options[:db].to_i
|
341
|
+
options[:driver] = _parse_driver(options[:driver]) || Connection.drivers.last
|
342
|
+
|
343
|
+
options
|
344
|
+
end
|
345
|
+
|
346
|
+
def _parse_driver(driver)
|
347
|
+
driver = driver.to_s if driver.is_a?(Symbol)
|
348
|
+
|
349
|
+
if driver.kind_of?(String)
|
350
|
+
case driver
|
351
|
+
when "ruby"
|
352
|
+
require "redis/connection/ruby"
|
353
|
+
driver = Connection::Ruby
|
354
|
+
when "hiredis"
|
355
|
+
require "redis/connection/hiredis"
|
356
|
+
driver = Connection::Hiredis
|
357
|
+
when "synchrony"
|
358
|
+
require "redis/connection/synchrony"
|
359
|
+
driver = Connection::Synchrony
|
360
|
+
else
|
361
|
+
raise "Unknown driver: #{driver}"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
driver
|
366
|
+
end
|
279
367
|
end
|
280
368
|
end
|
@@ -6,42 +6,53 @@ require "timeout"
|
|
6
6
|
class Redis
|
7
7
|
module Connection
|
8
8
|
class Hiredis
|
9
|
-
def initialize
|
10
|
-
@connection = ::Hiredis::Connection.new
|
11
|
-
end
|
12
9
|
|
13
|
-
def
|
14
|
-
|
10
|
+
def self.connect(config)
|
11
|
+
connection = ::Hiredis::Connection.new
|
12
|
+
|
13
|
+
if config[:scheme] == "unix"
|
14
|
+
connection.connect_unix(config[:path], Integer(config[:timeout] * 1_000_000))
|
15
|
+
else
|
16
|
+
connection.connect(config[:host], config[:port], Integer(config[:timeout] * 1_000_000))
|
17
|
+
end
|
18
|
+
|
19
|
+
instance = new(connection)
|
20
|
+
instance.timeout = config[:timeout]
|
21
|
+
instance
|
22
|
+
rescue Errno::ETIMEDOUT
|
23
|
+
raise TimeoutError
|
15
24
|
end
|
16
25
|
|
17
|
-
def
|
18
|
-
@connection
|
26
|
+
def initialize(connection)
|
27
|
+
@connection = connection
|
19
28
|
end
|
20
29
|
|
21
|
-
def
|
22
|
-
@connection
|
23
|
-
rescue Errno::ETIMEDOUT
|
24
|
-
raise Timeout::Error
|
30
|
+
def connected?
|
31
|
+
@connection && @connection.connected?
|
25
32
|
end
|
26
33
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
raise Timeout::Error
|
34
|
+
def timeout=(timeout)
|
35
|
+
# Hiredis works with microsecond timeouts
|
36
|
+
@connection.timeout = Integer(timeout * 1_000_000)
|
31
37
|
end
|
32
38
|
|
33
39
|
def disconnect
|
34
40
|
@connection.disconnect
|
41
|
+
@connection = nil
|
35
42
|
end
|
36
43
|
|
37
44
|
def write(command)
|
38
45
|
@connection.write(command.flatten(1))
|
46
|
+
rescue Errno::EAGAIN
|
47
|
+
raise TimeoutError
|
39
48
|
end
|
40
49
|
|
41
50
|
def read
|
42
51
|
reply = @connection.read
|
43
52
|
reply = CommandError.new(reply.message) if reply.is_a?(RuntimeError)
|
44
53
|
reply
|
54
|
+
rescue Errno::EAGAIN
|
55
|
+
raise TimeoutError
|
45
56
|
rescue RuntimeError => err
|
46
57
|
raise ProtocolError.new(err.message)
|
47
58
|
end
|
@@ -5,6 +5,155 @@ require "socket"
|
|
5
5
|
|
6
6
|
class Redis
|
7
7
|
module Connection
|
8
|
+
module SocketMixin
|
9
|
+
|
10
|
+
CRLF = "\r\n".freeze
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super(*args)
|
14
|
+
|
15
|
+
@timeout = nil
|
16
|
+
@buffer = ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def timeout=(timeout)
|
20
|
+
if timeout && timeout > 0
|
21
|
+
@timeout = timeout
|
22
|
+
else
|
23
|
+
@timeout = nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def read(nbytes)
|
28
|
+
result = @buffer.slice!(0, nbytes)
|
29
|
+
|
30
|
+
while result.bytesize < nbytes
|
31
|
+
result << _read_from_socket(nbytes - result.bytesize)
|
32
|
+
end
|
33
|
+
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def gets
|
38
|
+
crlf = nil
|
39
|
+
|
40
|
+
while (crlf = @buffer.index(CRLF)) == nil
|
41
|
+
@buffer << _read_from_socket(1024)
|
42
|
+
end
|
43
|
+
|
44
|
+
@buffer.slice!(0, crlf + CRLF.bytesize)
|
45
|
+
end
|
46
|
+
|
47
|
+
def _read_from_socket(nbytes)
|
48
|
+
begin
|
49
|
+
read_nonblock(nbytes)
|
50
|
+
|
51
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
52
|
+
if IO.select([self], nil, nil, @timeout)
|
53
|
+
retry
|
54
|
+
else
|
55
|
+
raise Redis::TimeoutError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
rescue EOFError
|
60
|
+
raise Errno::ECONNRESET
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
65
|
+
|
66
|
+
require "timeout"
|
67
|
+
|
68
|
+
class TCPSocket < ::TCPSocket
|
69
|
+
|
70
|
+
include SocketMixin
|
71
|
+
|
72
|
+
def self.connect(host, port, timeout)
|
73
|
+
Timeout.timeout(timeout) do
|
74
|
+
sock = new(host, port)
|
75
|
+
sock
|
76
|
+
end
|
77
|
+
rescue Timeout::Error
|
78
|
+
raise TimeoutError
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class UNIXSocket < ::UNIXSocket
|
83
|
+
|
84
|
+
# This class doesn't include the mixin, because JRuby raises
|
85
|
+
# Errno::EAGAIN on #read_nonblock even when IO.select says it is
|
86
|
+
# readable. This behavior shows in 1.6.6 in both 1.8 and 1.9 mode.
|
87
|
+
# Therefore, fall back on the default Unix socket implementation,
|
88
|
+
# without timeouts.
|
89
|
+
|
90
|
+
def self.connect(path, timeout)
|
91
|
+
Timeout.timeout(timeout) do
|
92
|
+
sock = new(path)
|
93
|
+
sock
|
94
|
+
end
|
95
|
+
rescue Timeout::Error
|
96
|
+
raise TimeoutError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
else
|
101
|
+
|
102
|
+
class TCPSocket < ::Socket
|
103
|
+
|
104
|
+
include SocketMixin
|
105
|
+
|
106
|
+
def self.connect(host, port, timeout)
|
107
|
+
# Limit lookup to IPv4, as Redis doesn't yet do IPv6...
|
108
|
+
addr = ::Socket.getaddrinfo(host, nil, Socket::AF_INET)
|
109
|
+
sock = new(::Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
110
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, addr[0][3])
|
111
|
+
|
112
|
+
begin
|
113
|
+
sock.connect_nonblock(sockaddr)
|
114
|
+
rescue Errno::EINPROGRESS
|
115
|
+
if IO.select(nil, [sock], nil, timeout) == nil
|
116
|
+
raise TimeoutError
|
117
|
+
end
|
118
|
+
|
119
|
+
begin
|
120
|
+
sock.connect_nonblock(sockaddr)
|
121
|
+
rescue Errno::EISCONN
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
sock
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class UNIXSocket < ::Socket
|
130
|
+
|
131
|
+
# This class doesn't include the mixin to keep its behavior in sync
|
132
|
+
# with the JRuby implementation.
|
133
|
+
|
134
|
+
def self.connect(path, timeout)
|
135
|
+
sock = new(::Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
|
136
|
+
sockaddr = ::Socket.pack_sockaddr_un(path)
|
137
|
+
|
138
|
+
begin
|
139
|
+
sock.connect_nonblock(sockaddr)
|
140
|
+
rescue Errno::EINPROGRESS
|
141
|
+
if IO.select(nil, [sock], nil, timeout) == nil
|
142
|
+
raise TimeoutError
|
143
|
+
end
|
144
|
+
|
145
|
+
begin
|
146
|
+
sock.connect_nonblock(sockaddr)
|
147
|
+
rescue Errno::EISCONN
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
sock
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
8
157
|
class Ruby
|
9
158
|
include Redis::Connection::CommandHelper
|
10
159
|
|
@@ -14,25 +163,24 @@ class Redis
|
|
14
163
|
DOLLAR = "$".freeze
|
15
164
|
ASTERISK = "*".freeze
|
16
165
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
166
|
+
def self.connect(config)
|
167
|
+
if config[:scheme] == "unix"
|
168
|
+
sock = UNIXSocket.connect(config[:path], config[:timeout])
|
169
|
+
else
|
170
|
+
sock = TCPSocket.connect(config[:host], config[:port], config[:timeout])
|
171
|
+
end
|
20
172
|
|
21
|
-
|
22
|
-
|
173
|
+
instance = new(sock)
|
174
|
+
instance.timeout = config[:timeout]
|
175
|
+
instance
|
23
176
|
end
|
24
177
|
|
25
|
-
def
|
26
|
-
|
27
|
-
@sock = TCPSocket.new(host, port)
|
28
|
-
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
29
|
-
end
|
178
|
+
def initialize(sock)
|
179
|
+
@sock = sock
|
30
180
|
end
|
31
181
|
|
32
|
-
def
|
33
|
-
|
34
|
-
@sock = UNIXSocket.new(path)
|
35
|
-
end
|
182
|
+
def connected?
|
183
|
+
!! @sock
|
36
184
|
end
|
37
185
|
|
38
186
|
def disconnect
|
@@ -42,16 +190,9 @@ class Redis
|
|
42
190
|
@sock = nil
|
43
191
|
end
|
44
192
|
|
45
|
-
def timeout=(
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
optval = [secs, usecs].pack("l_2")
|
50
|
-
|
51
|
-
begin
|
52
|
-
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
53
|
-
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
54
|
-
rescue Errno::ENOPROTOOPT
|
193
|
+
def timeout=(timeout)
|
194
|
+
if @sock.respond_to?(:timeout=)
|
195
|
+
@sock.timeout = timeout
|
55
196
|
end
|
56
197
|
end
|
57
198
|
|
@@ -60,13 +201,12 @@ class Redis
|
|
60
201
|
end
|
61
202
|
|
62
203
|
def read
|
63
|
-
|
64
|
-
|
65
|
-
reply_type
|
204
|
+
line = @sock.gets
|
205
|
+
reply_type = line.slice!(0, 1)
|
206
|
+
format_reply(reply_type, line)
|
66
207
|
|
67
|
-
|
68
|
-
|
69
|
-
format_reply(reply_type, @sock.gets)
|
208
|
+
rescue Errno::EAGAIN
|
209
|
+
raise TimeoutError
|
70
210
|
end
|
71
211
|
|
72
212
|
def format_reply(reply_type, line)
|
@@ -106,29 +246,6 @@ class Redis
|
|
106
246
|
|
107
247
|
Array.new(n) { read }
|
108
248
|
end
|
109
|
-
|
110
|
-
protected
|
111
|
-
|
112
|
-
begin
|
113
|
-
require "system_timer"
|
114
|
-
|
115
|
-
def with_timeout(seconds, &block)
|
116
|
-
SystemTimer.timeout_after(seconds, &block)
|
117
|
-
end
|
118
|
-
|
119
|
-
rescue LoadError
|
120
|
-
if ! defined?(RUBY_ENGINE)
|
121
|
-
# MRI 1.8, all other interpreters define RUBY_ENGINE, JRuby and
|
122
|
-
# Rubinius should have no issues with timeout.
|
123
|
-
warn "WARNING: using the built-in Timeout class which is known to have issues when used for opening connections. Install the SystemTimer gem if you want to make sure the Redis client will not hang."
|
124
|
-
end
|
125
|
-
|
126
|
-
require "timeout"
|
127
|
-
|
128
|
-
def with_timeout(seconds, &block)
|
129
|
-
Timeout.timeout(seconds, &block)
|
130
|
-
end
|
131
|
-
end
|
132
249
|
end
|
133
250
|
end
|
134
251
|
end
|