Skip to content

Commit 1f1850a

Browse files
committed
Merge remote-tracking branch 'redis-rb/master' into reconnect_attempts_to_fixnum
2 parents e16a838 + 71efca9 commit 1f1850a

File tree

11 files changed

+159
-23
lines changed

11 files changed

+159
-23
lines changed

.travis.yml

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,48 @@ matrix:
3838
# hiredis
3939
- rvm: jruby-18mode
4040
gemfile: .travis/Gemfile
41-
env: conn=hiredis REDIS_BRANCH=2.8
41+
env: conn=hiredis REDIS_BRANCH=3.0
42+
- rvm: jruby-18mode
43+
gemfile: .travis/Gemfile
44+
env: conn=hiredis REDIS_BRANCH=3.2
45+
- rvm: jruby-19mode
46+
gemfile: .travis/Gemfile
47+
env: conn=hiredis REDIS_BRANCH=3.0
4248
- rvm: jruby-19mode
4349
gemfile: .travis/Gemfile
44-
env: conn=hiredis REDIS_BRANCH=2.8
50+
env: conn=hiredis REDIS_BRANCH=3.2
4551
- rvm: jruby-9.0.5.0
4652
gemfile: .travis/Gemfile
47-
env: conn=hiredis REDIS_BRANCH=2.8
53+
env: conn=hiredis REDIS_BRANCH=3.0
54+
- rvm: jruby-9.0.5.0
55+
gemfile: .travis/Gemfile
56+
env: conn=hiredis REDIS_BRANCH=3.2
4857

4958
# synchrony
5059
- rvm: 1.8.7
5160
gemfile: .travis/Gemfile
52-
env: conn=synchrony REDIS_BRANCH=2.8
61+
env: conn=synchrony REDIS_BRANCH=3.0
62+
- rvm: 1.8.7
63+
gemfile: .travis/Gemfile
64+
env: conn=synchrony REDIS_BRANCH=3.2
65+
- rvm: jruby-18mode
66+
gemfile: .travis/Gemfile
67+
env: conn=synchrony REDIS_BRANCH=3.0
5368
- rvm: jruby-18mode
5469
gemfile: .travis/Gemfile
55-
env: conn=synchrony REDIS_BRANCH=2.8
70+
env: conn=synchrony REDIS_BRANCH=3.2
5671
- rvm: jruby-19mode
5772
gemfile: .travis/Gemfile
58-
env: conn=synchrony REDIS_BRANCH=2.8
73+
env: conn=synchrony REDIS_BRANCH=3.0
74+
- rvm: jruby-19mode
75+
gemfile: .travis/Gemfile
76+
env: conn=synchrony REDIS_BRANCH=3.2
77+
- rvm: jruby-9.0.5.0
78+
gemfile: .travis/Gemfile
79+
env: conn=synchrony REDIS_BRANCH=3.0
5980
- rvm: jruby-9.0.5.0
6081
gemfile: .travis/Gemfile
61-
env: conn=synchrony REDIS_BRANCH=2.8
82+
env: conn=synchrony REDIS_BRANCH=3.2
6283
allow_failures:
6384
- rvm: rbx-2
6485

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212
security updates in June of 2013; continuing to support it would prevent
1313
the use of newer features of Ruby.
1414

15+
# 3.3.3
16+
17+
* Improved timeout handling after dropping Timeout module.
18+
19+
# 3.3.2
20+
21+
* Added support for SPOP with COUNT. See #628.
22+
23+
* Fixed connection glitches when using SSL. See #644.
24+
25+
# 3.3.1
26+
27+
* Remove usage of Timeout::timeout, refactor into using low level non-blocking writes.
28+
This fixes a memory leak due to Timeout creating threads on each invocation.
29+
1530
# 3.3.0
1631

1732
* Added support for SSL/TLS. Redis doesn't support SSL natively, so you still

lib/redis.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,13 +1336,18 @@ def srem(key, member)
13361336
end
13371337
end
13381338

1339-
# Remove and return a random member from a set.
1339+
# Remove and return one or more random member from a set.
13401340
#
13411341
# @param [String] key
13421342
# @return [String]
1343-
def spop(key)
1343+
# @param [Fixnum] count
1344+
def spop(key, count = nil)
13441345
synchronize do |client|
1345-
client.call([:spop, key])
1346+
if count.nil?
1347+
client.call([:spop, key])
1348+
else
1349+
client.call([:spop, key, count])
1350+
end
13461351
end
13471352
end
13481353

lib/redis/client.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,12 @@ def _parse_options(options)
453453
case options[:tcp_keepalive]
454454
when Hash
455455
[:time, :intvl, :probes].each do |key|
456-
unless options[:tcp_keepalive][key].is_a?(Fixnum)
457-
raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum"
456+
unless options[:tcp_keepalive][key].is_a?(Integer)
457+
raise "Expected the #{key.inspect} key in :tcp_keepalive to be an Integer"
458458
end
459459
end
460460

461-
when Fixnum
461+
when Integer
462462
if options[:tcp_keepalive] >= 60
463463
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 20, :intvl => 10, :probes => 2}
464464

lib/redis/connection/ruby.rb

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,30 @@
1010
# Not all systems have OpenSSL support
1111
end
1212

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+
1323
class Redis
1424
module Connection
1525
module SocketMixin
1626

1727
CRLF = "\r\n".freeze
1828

1929
# Exceptions raised during non-blocking I/O ops that require retrying the op
20-
NBIO_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
21-
NBIO_EXCEPTIONS << IO::WaitReadable if RUBY_VERSION >= "1.9.3"
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
2237

2338
def initialize(*args)
2439
super(*args)
@@ -68,21 +83,58 @@ def _read_from_socket(nbytes)
6883
begin
6984
read_nonblock(nbytes)
7085

71-
rescue *NBIO_EXCEPTIONS
86+
rescue *NBIO_READ_EXCEPTIONS
7287
if IO.select([self], nil, nil, @timeout)
7388
retry
7489
else
7590
raise Redis::TimeoutError
7691
end
92+
rescue *NBIO_WRITE_EXCEPTIONS
93+
if IO.select(nil, [self], nil, @timeout)
94+
retry
95+
else
96+
raise Redis::TimeoutError
97+
end
7798
end
7899

79100
rescue EOFError
80101
raise Errno::ECONNRESET
81102
end
82103

83-
# UNIXSocket and TCPSocket don't support write timeouts
84-
def write(*args)
85-
Timeout.timeout(@write_timeout, TimeoutError) { super }
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
86138
end
87139
end
88140

@@ -255,6 +307,7 @@ def self.connect(config)
255307
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
256308
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
257309
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"
258311
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
259312
else
260313
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])

lib/redis/distributed.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,8 @@ def srem(key, member)
483483
end
484484

485485
# Remove and return a random member from a set.
486-
def spop(key)
487-
node_for(key).spop(key)
486+
def spop(key, count = nil)
487+
node_for(key).spop(key, count)
488488
end
489489

490490
# Get a random member from a set.

lib/redis/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class Redis
2-
VERSION = "3.3.0"
2+
VERSION = "3.3.3"
33
end

test/internals_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
class TestInternals < Test::Unit::TestCase
66

77
include Helper::Client
8+
include Helper::Skipable
89

910
def test_logger
1011
r.ping
@@ -160,6 +161,25 @@ def test_connection_timeout
160161
assert (Time.now - start_time) <= opts[:timeout]
161162
end
162163

164+
driver(:ruby) do
165+
def test_write_timeout
166+
return skip("Relies on buffer sizes, might be unreliable")
167+
168+
server = TCPServer.new("127.0.0.1", 0)
169+
port = server.addr[1]
170+
171+
# Hacky, but we need the buffer size
172+
val = TCPSocket.new("127.0.0.1", port).getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).unpack("i")[0]
173+
174+
assert_raise(Redis::TimeoutError) do
175+
Timeout.timeout(1) do
176+
redis = Redis.new(:port => port, :timeout => 5, :write_timeout => 0.1)
177+
redis.set("foo", "1" * val*2)
178+
end
179+
end
180+
end
181+
end
182+
163183
def close_on_ping(seq, options = {})
164184
$request = 0
165185

test/lint/sets.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ def test_spop
5252
assert_equal nil, r.spop("foo")
5353
end
5454

55+
def test_spop_with_positive_count
56+
target_version "3.2.0" do
57+
r.sadd "foo", "s1"
58+
r.sadd "foo", "s2"
59+
r.sadd "foo", "s3"
60+
r.sadd "foo", "s4"
61+
62+
pops = r.spop("foo", 3)
63+
64+
assert !(["s1", "s2", "s3", "s4"] & pops).empty?
65+
assert_equal 3, pops.size
66+
assert_equal 1, r.scard("foo")
67+
end
68+
end
69+
5570
def test_scard
5671
assert_equal 0, r.scard("foo")
5772

test/remote_server_control_commands_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_object
101101
assert_equal 1, r.object(:refcount, "list")
102102
encoding = r.object(:encoding, "list")
103103
assert "ziplist" == encoding || "quicklist" == encoding, "Wrong encoding for list"
104-
assert r.object(:idletime, "list").kind_of?(Fixnum)
104+
assert r.object(:idletime, "list").kind_of?(Integer)
105105
end
106106

107107
def test_sync

0 commit comments

Comments
 (0)