redis 3.3.5 → 4.0.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/.travis.yml +36 -52
- data/.travis/Gemfile +3 -1
- data/CHANGELOG.md +8 -6
- data/Gemfile +0 -1
- data/README.md +31 -75
- data/benchmarking/logging.rb +1 -1
- data/bors.toml +14 -0
- data/lib/redis.rb +68 -41
- data/lib/redis/client.rb +12 -8
- data/lib/redis/connection.rb +2 -2
- data/lib/redis/connection/command_helper.rb +2 -8
- data/lib/redis/connection/hiredis.rb +2 -2
- data/lib/redis/connection/ruby.rb +8 -28
- data/lib/redis/connection/synchrony.rb +12 -4
- data/lib/redis/distributed.rb +3 -3
- data/lib/redis/hash_ring.rb +20 -64
- data/lib/redis/pipeline.rb +0 -6
- data/lib/redis/version.rb +1 -1
- data/makefile +42 -0
- data/redis.gemspec +7 -9
- data/test/bitpos_test.rb +13 -19
- data/test/blocking_commands_test.rb +3 -5
- data/test/client_test.rb +1 -1
- data/test/command_map_test.rb +3 -5
- data/test/commands_on_hashes_test.rb +2 -4
- data/test/commands_on_hyper_log_log_test.rb +3 -5
- data/test/commands_on_lists_test.rb +2 -4
- data/test/commands_on_sets_test.rb +2 -4
- data/test/commands_on_sorted_sets_test.rb +17 -4
- data/test/commands_on_strings_test.rb +3 -5
- data/test/commands_on_value_types_test.rb +4 -6
- data/test/connection_handling_test.rb +5 -7
- data/test/distributed_blocking_commands_test.rb +2 -4
- data/test/distributed_commands_on_hashes_test.rb +2 -4
- data/test/distributed_commands_on_hyper_log_log_test.rb +2 -4
- data/test/distributed_commands_on_lists_test.rb +2 -4
- data/test/distributed_commands_on_sets_test.rb +2 -4
- data/test/distributed_commands_on_sorted_sets_test.rb +2 -4
- data/test/distributed_commands_on_strings_test.rb +2 -4
- data/test/distributed_commands_on_value_types_test.rb +2 -4
- data/test/distributed_commands_requiring_clustering_test.rb +1 -3
- data/test/distributed_connection_handling_test.rb +1 -3
- data/test/distributed_internals_test.rb +8 -19
- data/test/distributed_key_tags_test.rb +4 -6
- data/test/distributed_persistence_control_commands_test.rb +1 -3
- data/test/distributed_publish_subscribe_test.rb +1 -3
- data/test/distributed_remote_server_control_commands_test.rb +1 -3
- data/test/distributed_scripting_test.rb +1 -3
- data/test/distributed_sorting_test.rb +1 -3
- data/test/distributed_test.rb +12 -14
- data/test/distributed_transactions_test.rb +1 -3
- data/test/encoding_test.rb +4 -8
- data/test/error_replies_test.rb +2 -4
- data/test/fork_safety_test.rb +1 -6
- data/test/helper.rb +10 -41
- data/test/helper_test.rb +1 -3
- data/test/internals_test.rb +67 -55
- data/test/lint/strings.rb +6 -20
- data/test/lint/value_types.rb +8 -0
- data/test/persistence_control_commands_test.rb +1 -3
- data/test/pipelining_commands_test.rb +4 -8
- data/test/publish_subscribe_test.rb +1 -3
- data/test/remote_server_control_commands_test.rb +60 -3
- data/test/scanning_test.rb +1 -7
- data/test/scripting_test.rb +1 -3
- data/test/sentinel_command_test.rb +1 -3
- data/test/sentinel_test.rb +1 -3
- data/test/sorting_test.rb +1 -3
- data/test/ssl_test.rb +45 -49
- data/test/support/connection/hiredis.rb +1 -1
- data/test/support/connection/ruby.rb +1 -1
- data/test/support/connection/synchrony.rb +1 -1
- data/test/synchrony_driver.rb +6 -9
- data/test/thread_safety_test.rb +1 -3
- data/test/transactions_test.rb +1 -3
- data/test/unknown_commands_test.rb +1 -3
- data/test/url_param_test.rb +44 -46
- metadata +30 -18
- data/Rakefile +0 -87
- data/test/connection_test.rb +0 -57
data/lib/redis/client.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "errors"
|
2
2
|
require "socket"
|
3
3
|
require "cgi"
|
4
4
|
|
@@ -21,9 +21,7 @@ class Redis
|
|
21
21
|
:inherit_socket => false
|
22
22
|
}
|
23
23
|
|
24
|
-
|
25
|
-
Marshal.load(Marshal.dump(@options))
|
26
|
-
end
|
24
|
+
attr_reader :options
|
27
25
|
|
28
26
|
def scheme
|
29
27
|
@options[:scheme]
|
@@ -340,6 +338,7 @@ class Redis
|
|
340
338
|
Errno::EHOSTDOWN,
|
341
339
|
Errno::EHOSTUNREACH,
|
342
340
|
Errno::ENETUNREACH,
|
341
|
+
Errno::ENOENT,
|
343
342
|
Errno::ETIMEDOUT
|
344
343
|
|
345
344
|
raise CannotConnectError, "Error connecting to Redis on #{location} (#{$!.class})"
|
@@ -478,11 +477,16 @@ class Redis
|
|
478
477
|
|
479
478
|
if driver.kind_of?(String)
|
480
479
|
begin
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
480
|
+
require_relative "connection/#{driver}"
|
481
|
+
rescue LoadError, NameError => e
|
482
|
+
begin
|
483
|
+
require "connection/#{driver}"
|
484
|
+
rescue LoadError, NameError => e
|
485
|
+
raise RuntimeError, "Cannot load driver #{driver.inspect}: #{e.message}"
|
486
|
+
end
|
485
487
|
end
|
488
|
+
|
489
|
+
driver = Connection.const_get(driver.capitalize)
|
486
490
|
end
|
487
491
|
|
488
492
|
driver
|
data/lib/redis/connection.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "connection/registry"
|
2
2
|
|
3
3
|
# If a connection driver was required before this file, the array
|
4
4
|
# Redis::Connection.drivers will contain one or more classes. The last driver
|
@@ -6,4 +6,4 @@ require "redis/connection/registry"
|
|
6
6
|
# the plain Ruby driver as our default. Another driver can be required at a
|
7
7
|
# later point in time, causing it to be the last element of the #drivers array
|
8
8
|
# and therefore be chosen by default.
|
9
|
-
|
9
|
+
require_relative "connection/ruby" if Redis::Connection.drivers.empty?
|
@@ -30,14 +30,8 @@ class Redis
|
|
30
30
|
|
31
31
|
protected
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
string.force_encoding(Encoding::default_external)
|
36
|
-
end
|
37
|
-
else
|
38
|
-
def encode(string)
|
39
|
-
string
|
40
|
-
end
|
33
|
+
def encode(string)
|
34
|
+
string.force_encoding(Encoding.default_external)
|
41
35
|
end
|
42
36
|
end
|
43
37
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "registry"
|
2
|
+
require_relative "command_helper"
|
3
|
+
require_relative "../errors"
|
4
4
|
require "socket"
|
5
5
|
require "timeout"
|
6
6
|
|
@@ -10,36 +10,17 @@ rescue LoadError
|
|
10
10
|
# Not all systems have OpenSSL support
|
11
11
|
end
|
12
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
13
|
class Redis
|
24
14
|
module Connection
|
25
15
|
module SocketMixin
|
26
16
|
|
27
17
|
CRLF = "\r\n".freeze
|
28
18
|
|
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
19
|
def initialize(*args)
|
39
20
|
super(*args)
|
40
21
|
|
41
22
|
@timeout = @write_timeout = nil
|
42
|
-
@buffer = ""
|
23
|
+
@buffer = "".dup
|
43
24
|
end
|
44
25
|
|
45
26
|
def timeout=(timeout)
|
@@ -83,13 +64,13 @@ class Redis
|
|
83
64
|
begin
|
84
65
|
read_nonblock(nbytes)
|
85
66
|
|
86
|
-
rescue
|
67
|
+
rescue IO::WaitReadable
|
87
68
|
if IO.select([self], nil, nil, @timeout)
|
88
69
|
retry
|
89
70
|
else
|
90
71
|
raise Redis::TimeoutError
|
91
72
|
end
|
92
|
-
rescue
|
73
|
+
rescue IO::WaitWritable
|
93
74
|
if IO.select(nil, [self], nil, @timeout)
|
94
75
|
retry
|
95
76
|
else
|
@@ -105,13 +86,13 @@ class Redis
|
|
105
86
|
begin
|
106
87
|
write_nonblock(data)
|
107
88
|
|
108
|
-
rescue
|
89
|
+
rescue IO::WaitWritable
|
109
90
|
if IO.select(nil, [self], nil, @write_timeout)
|
110
91
|
retry
|
111
92
|
else
|
112
93
|
raise Redis::TimeoutError
|
113
94
|
end
|
114
|
-
rescue
|
95
|
+
rescue IO::WaitReadable
|
115
96
|
if IO.select([self], nil, nil, @write_timeout)
|
116
97
|
retry
|
117
98
|
else
|
@@ -307,7 +288,6 @@ class Redis
|
|
307
288
|
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
308
289
|
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
309
290
|
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
291
|
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
312
292
|
else
|
313
293
|
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "command_helper"
|
2
|
+
require_relative "registry"
|
3
|
+
require_relative "../errors"
|
4
4
|
require "em-synchrony"
|
5
5
|
require "hiredis/reader"
|
6
6
|
|
@@ -72,7 +72,15 @@ class Redis
|
|
72
72
|
|
73
73
|
def self.connect(config)
|
74
74
|
if config[:scheme] == "unix"
|
75
|
-
|
75
|
+
begin
|
76
|
+
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
77
|
+
rescue RuntimeError => e
|
78
|
+
if e.message == "no connection"
|
79
|
+
raise Errno::ECONNREFUSED
|
80
|
+
else
|
81
|
+
raise e
|
82
|
+
end
|
83
|
+
end
|
76
84
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
77
85
|
raise NotImplementedError, "SSL not supported by synchrony driver"
|
78
86
|
else
|
data/lib/redis/distributed.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "hash_ring"
|
2
2
|
|
3
3
|
class Redis
|
4
4
|
class Distributed
|
@@ -144,8 +144,8 @@ class Redis
|
|
144
144
|
end
|
145
145
|
|
146
146
|
# Create a key using the serialized value, previously obtained using DUMP.
|
147
|
-
def restore(key, ttl, serialized_value)
|
148
|
-
node_for(key).restore(key, ttl, serialized_value)
|
147
|
+
def restore(key, ttl, serialized_value, options = {})
|
148
|
+
node_for(key).restore(key, ttl, serialized_value, options)
|
149
149
|
end
|
150
150
|
|
151
151
|
# Transfer a key from the connected instance to another instance.
|
data/lib/redis/hash_ring.rb
CHANGED
@@ -25,7 +25,6 @@ class Redis
|
|
25
25
|
@nodes << node
|
26
26
|
@replicas.times do |i|
|
27
27
|
key = Zlib.crc32("#{node.id}:#{i}")
|
28
|
-
raise "Node ID collision" if @ring.has_key?(key)
|
29
28
|
@ring[key] = node
|
30
29
|
@sorted_keys << key
|
31
30
|
end
|
@@ -61,72 +60,29 @@ class Redis
|
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
while (lower <= upper) {
|
81
|
-
idx = (lower + upper) / 2;
|
82
|
-
|
83
|
-
VALUE continuumValue = RARRAY_PTR(ary)[idx];
|
84
|
-
unsigned int l = NUM2UINT(continuumValue);
|
85
|
-
if (l == r) {
|
86
|
-
return idx;
|
87
|
-
}
|
88
|
-
else if (l > r) {
|
89
|
-
upper = idx - 1;
|
90
|
-
}
|
91
|
-
else {
|
92
|
-
lower = idx + 1;
|
93
|
-
}
|
94
|
-
}
|
95
|
-
if (upper < 0) {
|
96
|
-
upper = RARRAY_LEN(ary) - 1;
|
97
|
-
}
|
98
|
-
return upper;
|
99
|
-
}
|
100
|
-
EOM
|
101
|
-
end
|
102
|
-
rescue Exception
|
103
|
-
# Find the closest index in HashRing with value <= the given value
|
104
|
-
def binary_search(ary, value, &block)
|
105
|
-
upper = ary.size - 1
|
106
|
-
lower = 0
|
107
|
-
idx = 0
|
108
|
-
|
109
|
-
while(lower <= upper) do
|
110
|
-
idx = (lower + upper) / 2
|
111
|
-
comp = ary[idx] <=> value
|
112
|
-
|
113
|
-
if comp == 0
|
114
|
-
return idx
|
115
|
-
elsif comp > 0
|
116
|
-
upper = idx - 1
|
117
|
-
else
|
118
|
-
lower = idx + 1
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
if upper < 0
|
123
|
-
upper = ary.size - 1
|
124
|
-
end
|
125
|
-
return upper
|
63
|
+
# Find the closest index in HashRing with value <= the given value
|
64
|
+
def self.binary_search(ary, value, &block)
|
65
|
+
upper = ary.size - 1
|
66
|
+
lower = 0
|
67
|
+
idx = 0
|
68
|
+
|
69
|
+
while(lower <= upper) do
|
70
|
+
idx = (lower + upper) / 2
|
71
|
+
comp = ary[idx] <=> value
|
72
|
+
|
73
|
+
if comp == 0
|
74
|
+
return idx
|
75
|
+
elsif comp > 0
|
76
|
+
upper = idx - 1
|
77
|
+
else
|
78
|
+
lower = idx + 1
|
126
79
|
end
|
80
|
+
end
|
127
81
|
|
82
|
+
if upper < 0
|
83
|
+
upper = ary.size - 1
|
128
84
|
end
|
85
|
+
return upper
|
129
86
|
end
|
130
|
-
|
131
87
|
end
|
132
88
|
end
|
data/lib/redis/pipeline.rb
CHANGED
data/lib/redis/version.rb
CHANGED
data/makefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
TEST_FILES := $(shell find test -name *_test.rb -type f)
|
2
|
+
REDIS_BRANCH := unstable
|
3
|
+
TMP := tmp
|
4
|
+
BUILD_DIR := ${TMP}/redis-${REDIS_BRANCH}
|
5
|
+
TARBALL := ${TMP}/redis-${REDIS_BRANCH}.tar.gz
|
6
|
+
BINARY := ${BUILD_DIR}/src/redis-server
|
7
|
+
PID_PATH := ${BUILD_DIR}/redis.pid
|
8
|
+
SOCKET_PATH := ${BUILD_DIR}/redis.sock
|
9
|
+
PORT := 6381
|
10
|
+
|
11
|
+
test: ${TEST_FILES}
|
12
|
+
make start
|
13
|
+
env SOCKET_PATH=${SOCKET_PATH} \
|
14
|
+
ruby -v $$(echo $? | tr ' ' '\n' | awk '{ print "-r./" $$0 }') -e ''
|
15
|
+
make stop
|
16
|
+
|
17
|
+
${TMP}:
|
18
|
+
mkdir $@
|
19
|
+
|
20
|
+
${TARBALL}: ${TMP}
|
21
|
+
wget https://github.com/antirez/redis/archive/${REDIS_BRANCH}.tar.gz -O $@
|
22
|
+
|
23
|
+
${BINARY}: ${TARBALL} ${TMP}
|
24
|
+
rm -rf ${BUILD_DIR}
|
25
|
+
mkdir -p ${BUILD_DIR}
|
26
|
+
tar xf ${TARBALL} -C ${TMP}
|
27
|
+
cd ${BUILD_DIR} && make
|
28
|
+
|
29
|
+
stop:
|
30
|
+
(test -f ${PID_PATH} && (kill $$(cat ${PID_PATH}) || true) && rm -f ${PID_PATH}) || true
|
31
|
+
|
32
|
+
start: ${BINARY}
|
33
|
+
${BINARY} \
|
34
|
+
--daemonize yes \
|
35
|
+
--pidfile ${PID_PATH} \
|
36
|
+
--port ${PORT} \
|
37
|
+
--unixsocket ${SOCKET_PATH}
|
38
|
+
|
39
|
+
clean:
|
40
|
+
(test -d ${BUILD_DIR} && cd ${BUILD_DIR}/src && make clean distclean) || true
|
41
|
+
|
42
|
+
.PHONY: test start stop
|
data/redis.gemspec
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$:.unshift File.expand_path("../lib", __FILE__)
|
4
|
-
|
5
|
-
require "redis/version"
|
1
|
+
require "./lib/redis/version"
|
6
2
|
|
7
3
|
Gem::Specification.new do |s|
|
8
4
|
s.name = "redis"
|
@@ -15,8 +11,7 @@ Gem::Specification.new do |s|
|
|
15
11
|
|
16
12
|
s.description = <<-EOS
|
17
13
|
A Ruby client that tries to match Redis' API one-to-one, while still
|
18
|
-
providing an idiomatic interface.
|
19
|
-
client-side sharding, pipelining, and an obsession for performance.
|
14
|
+
providing an idiomatic interface.
|
20
15
|
EOS
|
21
16
|
|
22
17
|
s.license = "MIT"
|
@@ -39,6 +34,9 @@ Gem::Specification.new do |s|
|
|
39
34
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
40
35
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
41
36
|
|
42
|
-
s.
|
43
|
-
|
37
|
+
s.required_ruby_version = '>= 2.2.2'
|
38
|
+
|
39
|
+
s.add_development_dependency("test-unit", ">= 3.1.5")
|
40
|
+
s.add_development_dependency("hiredis")
|
41
|
+
s.add_development_dependency("em-synchrony")
|
44
42
|
end
|
data/test/bitpos_test.rb
CHANGED
@@ -1,10 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
unless defined?(Enumerator)
|
6
|
-
Enumerator = Enumerable::Enumerator
|
7
|
-
end
|
1
|
+
require_relative "helper"
|
8
2
|
|
9
3
|
class TestBitpos < Test::Unit::TestCase
|
10
4
|
|
@@ -13,48 +7,48 @@ class TestBitpos < Test::Unit::TestCase
|
|
13
7
|
def test_bitpos_empty_zero
|
14
8
|
target_version "2.9.11" do
|
15
9
|
r.del "foo"
|
16
|
-
assert_equal
|
10
|
+
assert_equal(0, r.bitpos("foo", 0))
|
17
11
|
end
|
18
12
|
end
|
19
13
|
|
20
14
|
def test_bitpos_empty_one
|
21
15
|
target_version "2.9.11" do
|
22
16
|
r.del "foo"
|
23
|
-
assert_equal
|
17
|
+
assert_equal(-1, r.bitpos("foo", 1))
|
24
18
|
end
|
25
19
|
end
|
26
20
|
|
27
21
|
def test_bitpos_zero
|
28
22
|
target_version "2.9.11" do
|
29
23
|
r.set "foo", "\xff\xf0\x00"
|
30
|
-
assert_equal
|
24
|
+
assert_equal(12, r.bitpos("foo", 0))
|
31
25
|
end
|
32
26
|
end
|
33
27
|
|
34
28
|
def test_bitpos_one
|
35
29
|
target_version "2.9.11" do
|
36
30
|
r.set "foo", "\x00\x0f\x00"
|
37
|
-
assert_equal
|
31
|
+
assert_equal(12, r.bitpos("foo", 1))
|
38
32
|
end
|
39
33
|
end
|
40
34
|
|
41
35
|
def test_bitpos_zero_end_is_given
|
42
36
|
target_version "2.9.11" do
|
43
37
|
r.set "foo", "\xff\xff\xff"
|
44
|
-
assert_equal
|
45
|
-
assert_equal
|
46
|
-
assert_equal
|
38
|
+
assert_equal(24, r.bitpos("foo", 0))
|
39
|
+
assert_equal(24, r.bitpos("foo", 0, 0))
|
40
|
+
assert_equal(-1, r.bitpos("foo", 0, 0, -1))
|
47
41
|
end
|
48
42
|
end
|
49
43
|
|
50
44
|
def test_bitpos_one_intervals
|
51
45
|
target_version "2.9.11" do
|
52
46
|
r.set "foo", "\x00\xff\x00"
|
53
|
-
assert_equal
|
54
|
-
assert_equal
|
55
|
-
assert_equal
|
56
|
-
assert_equal
|
57
|
-
assert_equal
|
47
|
+
assert_equal(8, r.bitpos("foo", 1, 0, -1))
|
48
|
+
assert_equal(8, r.bitpos("foo", 1, 1, -1))
|
49
|
+
assert_equal(-1, r.bitpos("foo", 1, 2, -1))
|
50
|
+
assert_equal(-1, r.bitpos("foo", 1, 2, 200))
|
51
|
+
assert_equal(8, r.bitpos("foo", 1, 1, 1))
|
58
52
|
end
|
59
53
|
end
|
60
54
|
|