redis 3.0.0 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +269 -0
  3. data/README.md +295 -58
  4. data/lib/redis.rb +1760 -451
  5. data/lib/redis/client.rb +355 -88
  6. data/lib/redis/cluster.rb +295 -0
  7. data/lib/redis/cluster/command.rb +81 -0
  8. data/lib/redis/cluster/command_loader.rb +34 -0
  9. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  10. data/lib/redis/cluster/node.rb +107 -0
  11. data/lib/redis/cluster/node_key.rb +31 -0
  12. data/lib/redis/cluster/node_loader.rb +37 -0
  13. data/lib/redis/cluster/option.rb +90 -0
  14. data/lib/redis/cluster/slot.rb +86 -0
  15. data/lib/redis/cluster/slot_loader.rb +49 -0
  16. data/lib/redis/connection.rb +4 -2
  17. data/lib/redis/connection/command_helper.rb +5 -10
  18. data/lib/redis/connection/hiredis.rb +12 -8
  19. data/lib/redis/connection/registry.rb +2 -1
  20. data/lib/redis/connection/ruby.rb +232 -63
  21. data/lib/redis/connection/synchrony.rb +41 -14
  22. data/lib/redis/distributed.rb +205 -70
  23. data/lib/redis/errors.rb +48 -0
  24. data/lib/redis/hash_ring.rb +31 -73
  25. data/lib/redis/pipeline.rb +74 -18
  26. data/lib/redis/subscribe.rb +24 -13
  27. data/lib/redis/version.rb +3 -1
  28. metadata +63 -160
  29. data/.gitignore +0 -10
  30. data/.order +0 -169
  31. data/.travis.yml +0 -50
  32. data/.travis/Gemfile +0 -11
  33. data/.yardopts +0 -3
  34. data/Rakefile +0 -392
  35. data/benchmarking/logging.rb +0 -62
  36. data/benchmarking/pipeline.rb +0 -51
  37. data/benchmarking/speed.rb +0 -21
  38. data/benchmarking/suite.rb +0 -24
  39. data/benchmarking/worker.rb +0 -71
  40. data/examples/basic.rb +0 -15
  41. data/examples/dist_redis.rb +0 -43
  42. data/examples/incr-decr.rb +0 -17
  43. data/examples/list.rb +0 -26
  44. data/examples/pubsub.rb +0 -31
  45. data/examples/sets.rb +0 -36
  46. data/examples/unicorn/config.ru +0 -3
  47. data/examples/unicorn/unicorn.rb +0 -20
  48. data/redis.gemspec +0 -41
  49. data/test/blocking_commands_test.rb +0 -42
  50. data/test/command_map_test.rb +0 -30
  51. data/test/commands_on_hashes_test.rb +0 -21
  52. data/test/commands_on_lists_test.rb +0 -20
  53. data/test/commands_on_sets_test.rb +0 -77
  54. data/test/commands_on_sorted_sets_test.rb +0 -109
  55. data/test/commands_on_strings_test.rb +0 -83
  56. data/test/commands_on_value_types_test.rb +0 -99
  57. data/test/connection_handling_test.rb +0 -189
  58. data/test/db/.gitignore +0 -1
  59. data/test/distributed_blocking_commands_test.rb +0 -46
  60. data/test/distributed_commands_on_hashes_test.rb +0 -10
  61. data/test/distributed_commands_on_lists_test.rb +0 -22
  62. data/test/distributed_commands_on_sets_test.rb +0 -83
  63. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  64. data/test/distributed_commands_on_strings_test.rb +0 -48
  65. data/test/distributed_commands_on_value_types_test.rb +0 -87
  66. data/test/distributed_commands_requiring_clustering_test.rb +0 -148
  67. data/test/distributed_connection_handling_test.rb +0 -23
  68. data/test/distributed_internals_test.rb +0 -15
  69. data/test/distributed_key_tags_test.rb +0 -52
  70. data/test/distributed_persistence_control_commands_test.rb +0 -26
  71. data/test/distributed_publish_subscribe_test.rb +0 -92
  72. data/test/distributed_remote_server_control_commands_test.rb +0 -53
  73. data/test/distributed_scripting_test.rb +0 -102
  74. data/test/distributed_sorting_test.rb +0 -20
  75. data/test/distributed_test.rb +0 -58
  76. data/test/distributed_transactions_test.rb +0 -32
  77. data/test/encoding_test.rb +0 -18
  78. data/test/error_replies_test.rb +0 -59
  79. data/test/helper.rb +0 -188
  80. data/test/helper_test.rb +0 -22
  81. data/test/internals_test.rb +0 -214
  82. data/test/lint/blocking_commands.rb +0 -124
  83. data/test/lint/hashes.rb +0 -162
  84. data/test/lint/lists.rb +0 -143
  85. data/test/lint/sets.rb +0 -96
  86. data/test/lint/sorted_sets.rb +0 -201
  87. data/test/lint/strings.rb +0 -157
  88. data/test/lint/value_types.rb +0 -106
  89. data/test/persistence_control_commands_test.rb +0 -26
  90. data/test/pipelining_commands_test.rb +0 -195
  91. data/test/publish_subscribe_test.rb +0 -153
  92. data/test/remote_server_control_commands_test.rb +0 -104
  93. data/test/scripting_test.rb +0 -78
  94. data/test/sorting_test.rb +0 -45
  95. data/test/support/connection/hiredis.rb +0 -1
  96. data/test/support/connection/ruby.rb +0 -1
  97. data/test/support/connection/synchrony.rb +0 -17
  98. data/test/support/redis_mock.rb +0 -92
  99. data/test/support/wire/synchrony.rb +0 -24
  100. data/test/support/wire/thread.rb +0 -5
  101. data/test/synchrony_driver.rb +0 -57
  102. data/test/test.conf +0 -9
  103. data/test/thread_safety_test.rb +0 -32
  104. data/test/transactions_test.rb +0 -244
  105. data/test/unknown_commands_test.rb +0 -14
  106. data/test/url_param_test.rb +0 -64
@@ -1,20 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestDistributedSorting < Test::Unit::TestCase
6
-
7
- include Helper::Distributed
8
-
9
- def test_sort
10
- assert_raise(Redis::Distributed::CannotDistribute) do
11
- r.set("foo:1", "s1")
12
- r.set("foo:2", "s2")
13
-
14
- r.rpush("bar", "1")
15
- r.rpush("bar", "2")
16
-
17
- r.sort("bar", :get => "foo:*", :limit => [0, 1])
18
- end
19
- end
20
- end
@@ -1,58 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestDistributed < Test::Unit::TestCase
6
-
7
- include Helper::Distributed
8
-
9
- def test_handle_multiple_servers
10
- @r = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
11
-
12
- 100.times do |idx|
13
- @r.set(idx.to_s, "foo#{idx}")
14
- end
15
-
16
- 100.times do |idx|
17
- assert_equal "foo#{idx}", @r.get(idx.to_s)
18
- end
19
-
20
- assert_equal "0", @r.keys("*").sort.first
21
- assert_equal "string", @r.type("1")
22
- end
23
-
24
- def test_add_nodes
25
- logger = Logger.new("/dev/null")
26
-
27
- @r = Redis::Distributed.new NODES, :logger => logger, :timeout => 10
28
-
29
- assert_equal "127.0.0.1", @r.nodes[0].client.host
30
- assert_equal PORT, @r.nodes[0].client.port
31
- assert_equal 15, @r.nodes[0].client.db
32
- assert_equal 10, @r.nodes[0].client.timeout
33
- assert_equal logger, @r.nodes[0].client.logger
34
-
35
- @r.add_node("redis://localhost:6380/14")
36
-
37
- assert_equal "localhost", @r.nodes[1].client.host
38
- assert_equal 6380, @r.nodes[1].client.port
39
- assert_equal 14, @r.nodes[1].client.db
40
- assert_equal 10, @r.nodes[1].client.timeout
41
- assert_equal logger, @r.nodes[1].client.logger
42
- end
43
-
44
- def test_pipelining_commands_cannot_be_distributed
45
- assert_raise Redis::Distributed::CannotDistribute do
46
- r.pipelined do
47
- r.lpush "foo", "s1"
48
- r.lpush "foo", "s2"
49
- end
50
- end
51
- end
52
-
53
- def test_unknown_commands_does_not_work_by_default
54
- assert_raise NoMethodError do
55
- r.not_yet_implemented_command
56
- end
57
- end
58
- end
@@ -1,32 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestDistributedTransactions < Test::Unit::TestCase
6
-
7
- include Helper::Distributed
8
-
9
- def test_multi_discard
10
- @foo = nil
11
-
12
- assert_raise Redis::Distributed::CannotDistribute do
13
- r.multi { @foo = 1 }
14
- end
15
-
16
- assert_equal nil, @foo
17
-
18
- assert_raise Redis::Distributed::CannotDistribute do
19
- r.discard
20
- end
21
- end
22
-
23
- def test_watch_unwatch
24
- assert_raise Redis::Distributed::CannotDistribute do
25
- r.watch("foo")
26
- end
27
-
28
- assert_raise Redis::Distributed::CannotDistribute do
29
- r.unwatch
30
- end
31
- end
32
- end
@@ -1,18 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestEncoding < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- def test_returns_properly_encoded_strings
10
- if defined?(Encoding)
11
- with_external_encoding("UTF-8") do
12
- r.set "foo", "שלום"
13
-
14
- assert_equal "Shalom שלום", "Shalom " + r.get("foo")
15
- end
16
- end
17
- end
18
- end
@@ -1,59 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestErrorReplies < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- # Every test shouldn't disconnect from the server. Also, when error replies are
10
- # in play, the protocol should never get into an invalid state where there are
11
- # pending replies in the connection. Calling INFO after every test ensures that
12
- # the protocol is still in a valid state.
13
- def with_reconnection_check
14
- before = r.info["total_connections_received"]
15
- yield(r)
16
- after = r.info["total_connections_received"]
17
- ensure
18
- assert_equal before, after
19
- end
20
-
21
- def test_error_reply_for_single_command
22
- with_reconnection_check do
23
- begin
24
- r.unknown_command
25
- rescue => ex
26
- ensure
27
- assert ex.message =~ /unknown command/i
28
- end
29
- end
30
- end
31
-
32
- def test_raise_first_error_reply_in_pipeline
33
- with_reconnection_check do
34
- begin
35
- r.pipelined do
36
- r.set("foo", "s1")
37
- r.incr("foo") # not an integer
38
- r.lpush("foo", "value") # wrong kind of value
39
- end
40
- rescue => ex
41
- ensure
42
- assert ex.message =~ /not an integer/i
43
- end
44
- end
45
- end
46
-
47
- def test_recover_from_raise_in__call_loop
48
- with_reconnection_check do
49
- begin
50
- r.client.call_loop([:invalid_monitor]) do
51
- assert false # Should never be executed
52
- end
53
- rescue => ex
54
- ensure
55
- assert ex.message =~ /unknown command/i
56
- end
57
- end
58
- end
59
- end
@@ -1,188 +0,0 @@
1
- $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
-
3
- require "test/unit"
4
- require "logger"
5
- require "stringio"
6
-
7
- begin
8
- require "ruby-debug"
9
- rescue LoadError
10
- end
11
-
12
- $VERBOSE = true
13
-
14
- ENV["conn"] ||= "ruby"
15
-
16
- require "redis"
17
- require "redis/distributed"
18
- require "redis/connection/#{ENV["conn"]}"
19
-
20
- require "support/redis_mock"
21
- require "support/connection/#{ENV["conn"]}"
22
-
23
- PORT = 6381
24
- OPTIONS = {:port => PORT, :db => 15, :timeout => 0.1}
25
- NODES = ["redis://127.0.0.1:#{PORT}/15"]
26
-
27
- def init(redis)
28
- begin
29
- redis.select 14
30
- redis.flushdb
31
- redis.select 15
32
- redis.flushdb
33
- redis
34
- rescue Redis::CannotConnectError
35
- puts <<-EOS
36
-
37
- Cannot connect to Redis.
38
-
39
- Make sure Redis is running on localhost, port #{PORT}.
40
- This testing suite connects to the database 15.
41
-
42
- To install redis:
43
- visit <http://redis.io/download/>.
44
-
45
- To start the server:
46
- rake start
47
-
48
- To stop the server:
49
- rake stop
50
-
51
- EOS
52
- exit 1
53
- end
54
- end
55
-
56
- def driver(*drivers, &blk)
57
- if drivers.map(&:to_s).include?(ENV["conn"])
58
- class_eval(&blk)
59
- end
60
- end
61
-
62
- module Helper
63
-
64
- def run(runner)
65
- if respond_to?(:around)
66
- around { super(runner) }
67
- else
68
- super
69
- end
70
- end
71
-
72
- def silent
73
- verbose, $VERBOSE = $VERBOSE, false
74
-
75
- begin
76
- yield
77
- ensure
78
- $VERBOSE = verbose
79
- end
80
- end
81
-
82
- def with_external_encoding(encoding)
83
- original_encoding = Encoding.default_external
84
-
85
- begin
86
- silent { Encoding.default_external = Encoding.find(encoding) }
87
- yield
88
- ensure
89
- silent { Encoding.default_external = original_encoding }
90
- end
91
- end
92
-
93
- class Version
94
-
95
- include Comparable
96
-
97
- attr :parts
98
-
99
- def initialize(v)
100
- case v
101
- when Version
102
- @parts = v.parts
103
- else
104
- @parts = v.to_s.split(".")
105
- end
106
- end
107
-
108
- def <=>(other)
109
- other = Version.new(other)
110
- length = [self.parts.length, other.parts.length].max
111
- length.times do |i|
112
- a, b = self.parts[i], other.parts[i]
113
-
114
- return -1 if a.nil?
115
- return +1 if b.nil?
116
- return a <=> b if a != b
117
- end
118
-
119
- 0
120
- end
121
- end
122
-
123
- module Generic
124
-
125
- include Helper
126
-
127
- attr_reader :log
128
- attr_reader :redis
129
-
130
- alias :r :redis
131
-
132
- def setup
133
- @log = StringIO.new
134
- @redis = init _new_client
135
- end
136
-
137
- def teardown
138
- @redis.quit if @redis
139
- end
140
-
141
- def redis_mock(commands, options = {}, &blk)
142
- RedisMock.start(commands) do |port|
143
- yield _new_client(options.merge(:port => port))
144
- end
145
- end
146
- end
147
-
148
- module Client
149
-
150
- include Generic
151
-
152
- def version
153
- Version.new(redis.info["redis_version"])
154
- end
155
-
156
- private
157
-
158
- def _format_options(options)
159
- OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
160
- end
161
-
162
- def _new_client(options = {})
163
- Redis.new(_format_options(options))
164
- end
165
- end
166
-
167
- module Distributed
168
-
169
- include Generic
170
-
171
- def version
172
- Version.new(redis.info.first["redis_version"])
173
- end
174
-
175
- private
176
-
177
- def _format_options(options)
178
- {
179
- :timeout => OPTIONS[:timeout],
180
- :logger => ::Logger.new(@log),
181
- }.merge(options)
182
- end
183
-
184
- def _new_client(options = {})
185
- Redis::Distributed.new(NODES, _format_options(options))
186
- end
187
- end
188
- end
@@ -1,22 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestHelper < Test::Unit::TestCase
6
-
7
- include Helper
8
-
9
- def test_version_comparison
10
- v = Version.new("2.0.0")
11
-
12
- assert v < "3"
13
- assert v > "1"
14
- assert v > "2"
15
-
16
- assert v < "2.1"
17
- assert v < "2.0.1"
18
- assert v < "2.0.0.1"
19
-
20
- assert v == "2.0.0"
21
- end
22
- end
@@ -1,214 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
-
5
- class TestInternals < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- def test_logger
10
- r.ping
11
-
12
- assert log.string =~ /Redis >> PING/
13
- assert log.string =~ /Redis >> \d+\.\d+ms/
14
- end
15
-
16
- def test_logger_with_pipelining
17
- r.pipelined do
18
- r.set "foo", "bar"
19
- r.get "foo"
20
- end
21
-
22
- assert log.string["SET foo bar"]
23
- assert log.string["GET foo"]
24
- end
25
-
26
- def test_recovers_from_failed_commands
27
- # See https://github.com/redis/redis-rb/issues#issue/28
28
-
29
- assert_raise(Redis::CommandError) do
30
- r.command_that_doesnt_exist
31
- end
32
-
33
- assert_nothing_raised do
34
- r.info
35
- end
36
- end
37
-
38
- def test_raises_on_protocol_errors
39
- redis_mock(:ping => lambda { |*_| "foo" }) do |redis|
40
- assert_raise(Redis::ProtocolError) do
41
- redis.ping
42
- end
43
- end
44
- end
45
-
46
- def test_provides_a_meaningful_inspect
47
- assert_equal "#<Redis client v#{Redis::VERSION} for redis://127.0.0.1:#{PORT}/15>", r.inspect
48
- end
49
-
50
- def test_redis_current
51
- assert_equal "127.0.0.1", Redis.current.client.host
52
- assert_equal 6379, Redis.current.client.port
53
- assert_equal 0, Redis.current.client.db
54
-
55
- Redis.current = Redis.new(OPTIONS.merge(:port => 6380, :db => 1))
56
-
57
- t = Thread.new do
58
- assert_equal "127.0.0.1", Redis.current.client.host
59
- assert_equal 6380, Redis.current.client.port
60
- assert_equal 1, Redis.current.client.db
61
- end
62
-
63
- t.join
64
-
65
- assert_equal "127.0.0.1", Redis.current.client.host
66
- assert_equal 6380, Redis.current.client.port
67
- assert_equal 1, Redis.current.client.db
68
- end
69
-
70
- def test_default_id_with_host_and_port
71
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0))
72
- assert_equal "redis://host:1234/0", redis.client.id
73
- end
74
-
75
- def test_default_id_with_host_and_port_and_explicit_scheme
76
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0, :scheme => "foo"))
77
- assert_equal "redis://host:1234/0", redis.client.id
78
- end
79
-
80
- def test_default_id_with_path
81
- redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0))
82
- assert_equal "redis:///tmp/redis.sock/0", redis.client.id
83
- end
84
-
85
- def test_default_id_with_path_and_explicit_scheme
86
- redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0, :scheme => "foo"))
87
- assert_equal "redis:///tmp/redis.sock/0", redis.client.id
88
- end
89
-
90
- def test_override_id
91
- redis = Redis.new(OPTIONS.merge(:id => "test"))
92
- assert_equal redis.client.id, "test"
93
- end
94
-
95
- def test_timeout
96
- assert_nothing_raised do
97
- Redis.new(OPTIONS.merge(:timeout => 0))
98
- end
99
- end
100
-
101
- def test_time
102
- return if version < "2.5.4"
103
-
104
- # Test that the difference between the time that Ruby reports and the time
105
- # that Redis reports is minimal (prevents the test from being racy).
106
- rv = r.time
107
-
108
- redis_usec = rv[0] * 1_000_000 + rv[1]
109
- ruby_usec = Integer(Time.now.to_f * 1_000_000)
110
-
111
- assert 500_000 > (ruby_usec - redis_usec).abs
112
- end
113
-
114
- def test_connection_timeout
115
- assert_raise Redis::CannotConnectError do
116
- Redis.new(OPTIONS.merge(:host => "10.255.255.254", :timeout => 0.1)).ping
117
- end
118
- end
119
-
120
- def close_on_ping(seq)
121
- $request = 0
122
-
123
- command = lambda do
124
- idx = $request
125
- $request += 1
126
-
127
- rv = "+%d" % idx
128
- rv = nil if seq.include?(idx)
129
- rv
130
- end
131
-
132
- redis_mock(:ping => command, :timeout => 0.1) do |redis|
133
- yield(redis)
134
- end
135
- end
136
-
137
- def test_retry_by_default
138
- close_on_ping([0]) do |redis|
139
- assert_equal "1", redis.ping
140
- end
141
- end
142
-
143
- def test_retry_when_wrapped_in_with_reconnect_true
144
- close_on_ping([0]) do |redis|
145
- redis.with_reconnect(true) do
146
- assert_equal "1", redis.ping
147
- end
148
- end
149
- end
150
-
151
- def test_dont_retry_when_wrapped_in_with_reconnect_false
152
- close_on_ping([0]) do |redis|
153
- assert_raise Redis::ConnectionError do
154
- redis.with_reconnect(false) do
155
- redis.ping
156
- end
157
- end
158
- end
159
- end
160
-
161
- def test_dont_retry_when_wrapped_in_without_reconnect
162
- close_on_ping([0]) do |redis|
163
- assert_raise Redis::ConnectionError do
164
- redis.without_reconnect do
165
- redis.ping
166
- end
167
- end
168
- end
169
- end
170
-
171
- def test_retry_only_once_when_read_raises_econnreset
172
- close_on_ping([0, 1]) do |redis|
173
- assert_raise Redis::ConnectionError do
174
- redis.ping
175
- end
176
-
177
- assert !redis.client.connected?
178
- end
179
- end
180
-
181
- def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
182
- close_on_ping([1]) do |redis|
183
- assert_raise Redis::ConnectionError do
184
- redis.pipelined do
185
- redis.ping
186
- redis.ping # Second #read times out
187
- end
188
- end
189
-
190
- assert !redis.client.connected?
191
- end
192
- end
193
-
194
- def test_connecting_to_unix_domain_socket
195
- assert_nothing_raised do
196
- Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock")).ping
197
- end
198
- end
199
-
200
- driver(:ruby, :hiredis) do
201
- def test_bubble_timeout_without_retrying
202
- serv = TCPServer.new(6380)
203
-
204
- redis = Redis.new(:port => 6380, :timeout => 0.1)
205
-
206
- assert_raise(Redis::TimeoutError) do
207
- redis.ping
208
- end
209
-
210
- ensure
211
- serv.close if serv
212
- end
213
- end
214
- end