redis 4.0.0.rc1 → 4.4.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.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +143 -3
  3. data/README.md +127 -18
  4. data/lib/redis/client.rb +150 -93
  5. data/lib/redis/cluster/command.rb +81 -0
  6. data/lib/redis/cluster/command_loader.rb +34 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +108 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +37 -0
  11. data/lib/redis/cluster/option.rb +93 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +49 -0
  14. data/lib/redis/cluster.rb +291 -0
  15. data/lib/redis/connection/command_helper.rb +3 -2
  16. data/lib/redis/connection/hiredis.rb +4 -3
  17. data/lib/redis/connection/registry.rb +2 -1
  18. data/lib/redis/connection/ruby.rb +123 -105
  19. data/lib/redis/connection/synchrony.rb +18 -5
  20. data/lib/redis/connection.rb +2 -0
  21. data/lib/redis/distributed.rb +955 -0
  22. data/lib/redis/errors.rb +48 -0
  23. data/lib/redis/hash_ring.rb +89 -0
  24. data/lib/redis/pipeline.rb +55 -9
  25. data/lib/redis/subscribe.rb +11 -12
  26. data/lib/redis/version.rb +3 -1
  27. data/lib/redis.rb +1242 -381
  28. metadata +34 -141
  29. data/.gitignore +0 -16
  30. data/.travis/Gemfile +0 -11
  31. data/.travis.yml +0 -71
  32. data/.yardopts +0 -3
  33. data/Gemfile +0 -3
  34. data/benchmarking/logging.rb +0 -71
  35. data/benchmarking/pipeline.rb +0 -51
  36. data/benchmarking/speed.rb +0 -21
  37. data/benchmarking/suite.rb +0 -24
  38. data/benchmarking/worker.rb +0 -71
  39. data/examples/basic.rb +0 -15
  40. data/examples/consistency.rb +0 -114
  41. data/examples/incr-decr.rb +0 -17
  42. data/examples/list.rb +0 -26
  43. data/examples/pubsub.rb +0 -37
  44. data/examples/sentinel/sentinel.conf +0 -9
  45. data/examples/sentinel/start +0 -49
  46. data/examples/sentinel.rb +0 -41
  47. data/examples/sets.rb +0 -36
  48. data/examples/unicorn/config.ru +0 -3
  49. data/examples/unicorn/unicorn.rb +0 -20
  50. data/makefile +0 -42
  51. data/redis.gemspec +0 -40
  52. data/test/bitpos_test.rb +0 -63
  53. data/test/blocking_commands_test.rb +0 -183
  54. data/test/client_test.rb +0 -59
  55. data/test/command_map_test.rb +0 -28
  56. data/test/commands_on_hashes_test.rb +0 -174
  57. data/test/commands_on_hyper_log_log_test.rb +0 -70
  58. data/test/commands_on_lists_test.rb +0 -154
  59. data/test/commands_on_sets_test.rb +0 -208
  60. data/test/commands_on_sorted_sets_test.rb +0 -444
  61. data/test/commands_on_strings_test.rb +0 -338
  62. data/test/commands_on_value_types_test.rb +0 -246
  63. data/test/connection_handling_test.rb +0 -275
  64. data/test/db/.gitkeep +0 -0
  65. data/test/encoding_test.rb +0 -14
  66. data/test/error_replies_test.rb +0 -57
  67. data/test/fork_safety_test.rb +0 -60
  68. data/test/helper.rb +0 -179
  69. data/test/helper_test.rb +0 -22
  70. data/test/internals_test.rb +0 -435
  71. data/test/persistence_control_commands_test.rb +0 -24
  72. data/test/pipelining_commands_test.rb +0 -238
  73. data/test/publish_subscribe_test.rb +0 -280
  74. data/test/remote_server_control_commands_test.rb +0 -175
  75. data/test/scanning_test.rb +0 -407
  76. data/test/scripting_test.rb +0 -76
  77. data/test/sentinel_command_test.rb +0 -78
  78. data/test/sentinel_test.rb +0 -253
  79. data/test/sorting_test.rb +0 -57
  80. data/test/ssl_test.rb +0 -69
  81. data/test/support/connection/hiredis.rb +0 -1
  82. data/test/support/connection/ruby.rb +0 -1
  83. data/test/support/connection/synchrony.rb +0 -17
  84. data/test/support/redis_mock.rb +0 -130
  85. data/test/support/ssl/gen_certs.sh +0 -31
  86. data/test/support/ssl/trusted-ca.crt +0 -25
  87. data/test/support/ssl/trusted-ca.key +0 -27
  88. data/test/support/ssl/trusted-cert.crt +0 -81
  89. data/test/support/ssl/trusted-cert.key +0 -28
  90. data/test/support/ssl/untrusted-ca.crt +0 -26
  91. data/test/support/ssl/untrusted-ca.key +0 -27
  92. data/test/support/ssl/untrusted-cert.crt +0 -82
  93. data/test/support/ssl/untrusted-cert.key +0 -28
  94. data/test/support/wire/synchrony.rb +0 -24
  95. data/test/support/wire/thread.rb +0 -5
  96. data/test/synchrony_driver.rb +0 -85
  97. data/test/test.conf.erb +0 -9
  98. data/test/thread_safety_test.rb +0 -60
  99. data/test/transactions_test.rb +0 -262
  100. data/test/unknown_commands_test.rb +0 -12
  101. data/test/url_param_test.rb +0 -136
@@ -1,275 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestConnectionHandling < Test::Unit::TestCase
4
-
5
- include Helper::Client
6
-
7
- def test_auth
8
- commands = {
9
- :auth => lambda { |password| $auth = password; "+OK" },
10
- :get => lambda { |key| $auth == "secret" ? "$3\r\nbar" : "$-1" },
11
- }
12
-
13
- redis_mock(commands, :password => "secret") do |redis|
14
- assert_equal "bar", redis.get("foo")
15
- end
16
- end
17
-
18
- def test_id
19
- commands = {
20
- :client => lambda { |cmd, name| $name = [cmd, name]; "+OK" },
21
- :ping => lambda { "+PONG" },
22
- }
23
-
24
- redis_mock(commands, :id => "client-name") do |redis|
25
- assert_equal "PONG", redis.ping
26
- end
27
-
28
- assert_equal ["setname","client-name"], $name
29
- end
30
-
31
- def test_ping
32
- assert_equal "PONG", r.ping
33
- end
34
-
35
- def test_select
36
- r.set "foo", "bar"
37
-
38
- r.select 14
39
- assert_equal nil, r.get("foo")
40
-
41
- r._client.disconnect
42
-
43
- assert_equal nil, r.get("foo")
44
- end
45
-
46
- def test_quit
47
- r.quit
48
-
49
- assert !r._client.connected?
50
- end
51
-
52
- def test_close
53
- quit = 0
54
-
55
- commands = {
56
- :quit => lambda do
57
- quit += 1
58
- "+OK"
59
- end
60
- }
61
-
62
- redis_mock(commands) do |redis|
63
- assert_equal 0, quit
64
-
65
- redis.quit
66
-
67
- assert_equal 1, quit
68
-
69
- redis.ping
70
-
71
- redis.close
72
-
73
- assert_equal 1, quit
74
-
75
- assert !redis.connected?
76
- end
77
- end
78
-
79
- def test_disconnect
80
- quit = 0
81
-
82
- commands = {
83
- :quit => lambda do
84
- quit += 1
85
- "+OK"
86
- end
87
- }
88
-
89
- redis_mock(commands) do |redis|
90
- assert_equal 0, quit
91
-
92
- redis.quit
93
-
94
- assert_equal 1, quit
95
-
96
- redis.ping
97
-
98
- redis.disconnect!
99
-
100
- assert_equal 1, quit
101
-
102
- assert !redis.connected?
103
- end
104
- end
105
-
106
- def test_shutdown
107
- commands = {
108
- :shutdown => lambda { :exit }
109
- }
110
-
111
- redis_mock(commands) do |redis|
112
- # SHUTDOWN does not reply: test that it does not raise here.
113
- assert_equal nil, redis.shutdown
114
- end
115
- end
116
-
117
- def test_shutdown_with_error
118
- connections = 0
119
- commands = {
120
- :select => lambda { |*_| connections += 1; "+OK\r\n" },
121
- :connections => lambda { ":#{connections}\r\n" },
122
- :shutdown => lambda { "-ERR could not shutdown\r\n" }
123
- }
124
-
125
- redis_mock(commands) do |redis|
126
- connections = redis.connections
127
-
128
- # SHUTDOWN replies with an error: test that it gets raised
129
- assert_raise Redis::CommandError do
130
- redis.shutdown
131
- end
132
-
133
- # The connection should remain in tact
134
- assert_equal connections, redis.connections
135
- end
136
- end
137
-
138
- def test_shutdown_from_pipeline
139
- commands = {
140
- :shutdown => lambda { :exit }
141
- }
142
-
143
- redis_mock(commands) do |redis|
144
- result = redis.pipelined do
145
- redis.shutdown
146
- end
147
-
148
- assert_equal nil, result
149
- assert !redis._client.connected?
150
- end
151
- end
152
-
153
- def test_shutdown_with_error_from_pipeline
154
- connections = 0
155
- commands = {
156
- :select => lambda { |*_| connections += 1; "+OK\r\n" },
157
- :connections => lambda { ":#{connections}\r\n" },
158
- :shutdown => lambda { "-ERR could not shutdown\r\n" }
159
- }
160
-
161
- redis_mock(commands) do |redis|
162
- connections = redis.connections
163
-
164
- # SHUTDOWN replies with an error: test that it gets raised
165
- assert_raise Redis::CommandError do
166
- redis.pipelined do
167
- redis.shutdown
168
- end
169
- end
170
-
171
- # The connection should remain in tact
172
- assert_equal connections, redis.connections
173
- end
174
- end
175
-
176
- def test_shutdown_from_multi_exec
177
- commands = {
178
- :multi => lambda { "+OK\r\n" },
179
- :shutdown => lambda { "+QUEUED\r\n" },
180
- :exec => lambda { :exit }
181
- }
182
-
183
- redis_mock(commands) do |redis|
184
- result = redis.multi do
185
- redis.shutdown
186
- end
187
-
188
- assert_equal nil, result
189
- assert !redis._client.connected?
190
- end
191
- end
192
-
193
- def test_shutdown_with_error_from_multi_exec
194
- connections = 0
195
- commands = {
196
- :select => lambda { |*_| connections += 1; "+OK\r\n" },
197
- :connections => lambda { ":#{connections}\r\n" },
198
- :multi => lambda { "+OK\r\n" },
199
- :shutdown => lambda { "+QUEUED\r\n" },
200
- :exec => lambda { "*1\r\n-ERR could not shutdown\r\n" }
201
- }
202
-
203
- redis_mock(commands) do |redis|
204
- connections = redis.connections
205
-
206
- # SHUTDOWN replies with an error: test that it gets returned
207
- # We should test for Redis::CommandError here, but hiredis doesn't yet do
208
- # custom error classes.
209
- err = nil
210
-
211
- begin
212
- redis.multi { redis.shutdown }
213
- rescue => err
214
- end
215
-
216
- assert err.kind_of?(StandardError)
217
-
218
- # The connection should remain intact
219
- assert_equal connections, redis.connections
220
- end
221
- end
222
-
223
- def test_slaveof
224
- redis_mock(:slaveof => lambda { |host, port| "+SLAVEOF #{host} #{port}" }) do |redis|
225
- assert_equal "SLAVEOF somehost 6381", redis.slaveof("somehost", 6381)
226
- end
227
- end
228
-
229
- def test_bgrewriteaof
230
- redis_mock(:bgrewriteaof => lambda { "+BGREWRITEAOF" }) do |redis|
231
- assert_equal "BGREWRITEAOF", redis.bgrewriteaof
232
- end
233
- end
234
-
235
- def test_config_get
236
- assert r.config(:get, "*")["timeout"] != nil
237
-
238
- config = r.config(:get, "timeout")
239
- assert_equal ["timeout"], config.keys
240
- assert config.values.compact.size > 0
241
- end
242
-
243
- def test_config_set
244
- begin
245
- assert_equal "OK", r.config(:set, "timeout", 200)
246
- assert_equal "200", r.config(:get, "*")["timeout"]
247
-
248
- assert_equal "OK", r.config(:set, "timeout", 100)
249
- assert_equal "100", r.config(:get, "*")["timeout"]
250
- ensure
251
- r.config :set, "timeout", 300
252
- end
253
- end
254
-
255
- driver(:ruby, :hiredis) do
256
- def test_consistency_on_multithreaded_env
257
- t = nil
258
-
259
- commands = {
260
- :set => lambda { |key, value| t.kill; "+OK\r\n" },
261
- :incr => lambda { |key| ":1\r\n" },
262
- }
263
-
264
- redis_mock(commands) do |redis|
265
- t = Thread.new do
266
- redis.set("foo", "bar")
267
- end
268
-
269
- t.join
270
-
271
- assert_equal 1, redis.incr("baz")
272
- end
273
- end
274
- end
275
- end
data/test/db/.gitkeep DELETED
File without changes
@@ -1,14 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestEncoding < Test::Unit::TestCase
4
-
5
- include Helper::Client
6
-
7
- def test_returns_properly_encoded_strings
8
- with_external_encoding("UTF-8") do
9
- r.set "foo", "שלום"
10
-
11
- assert_equal "Shalom שלום", "Shalom " + r.get("foo")
12
- end
13
- end
14
- end
@@ -1,57 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestErrorReplies < Test::Unit::TestCase
4
-
5
- include Helper::Client
6
-
7
- # Every test shouldn't disconnect from the server. Also, when error replies are
8
- # in play, the protocol should never get into an invalid state where there are
9
- # pending replies in the connection. Calling INFO after every test ensures that
10
- # the protocol is still in a valid state.
11
- def with_reconnection_check
12
- before = r.info["total_connections_received"]
13
- yield(r)
14
- after = r.info["total_connections_received"]
15
- ensure
16
- assert_equal before, after
17
- end
18
-
19
- def test_error_reply_for_single_command
20
- with_reconnection_check do
21
- begin
22
- r.unknown_command
23
- rescue => ex
24
- ensure
25
- assert ex.message =~ /unknown command/i
26
- end
27
- end
28
- end
29
-
30
- def test_raise_first_error_reply_in_pipeline
31
- with_reconnection_check do
32
- begin
33
- r.pipelined do
34
- r.set("foo", "s1")
35
- r.incr("foo") # not an integer
36
- r.lpush("foo", "value") # wrong kind of value
37
- end
38
- rescue => ex
39
- ensure
40
- assert ex.message =~ /not an integer/i
41
- end
42
- end
43
- end
44
-
45
- def test_recover_from_raise_in__call_loop
46
- with_reconnection_check do
47
- begin
48
- r._client.call_loop([:invalid_monitor]) do
49
- assert false # Should never be executed
50
- end
51
- rescue => ex
52
- ensure
53
- assert ex.message =~ /unknown command/i
54
- end
55
- end
56
- end
57
- end
@@ -1,60 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestForkSafety < Test::Unit::TestCase
4
-
5
- include Helper::Client
6
-
7
- driver(:ruby, :hiredis) do
8
- def test_fork_safety
9
- redis = Redis.new(OPTIONS)
10
- redis.set "foo", 1
11
-
12
- child_pid = fork do
13
- begin
14
- # InheritedError triggers a reconnect,
15
- # so we need to disable reconnects to force
16
- # the exception bubble up
17
- redis.without_reconnect do
18
- redis.set "foo", 2
19
- end
20
- rescue Redis::InheritedError
21
- exit 127
22
- end
23
- end
24
-
25
- _, status = Process.wait2(child_pid)
26
-
27
- assert_equal 127, status.exitstatus
28
- assert_equal "1", redis.get("foo")
29
-
30
- rescue NotImplementedError => error
31
- raise unless error.message =~ /fork is not available/
32
- end
33
-
34
- def test_fork_safety_with_enabled_inherited_socket
35
- redis = Redis.new(OPTIONS.merge(:inherit_socket => true))
36
- redis.set "foo", 1
37
-
38
- child_pid = fork do
39
- begin
40
- # InheritedError triggers a reconnect,
41
- # so we need to disable reconnects to force
42
- # the exception bubble up
43
- redis.without_reconnect do
44
- redis.set "foo", 2
45
- end
46
- rescue Redis::InheritedError
47
- exit 127
48
- end
49
- end
50
-
51
- _, status = Process.wait2(child_pid)
52
-
53
- assert_equal 0, status.exitstatus
54
- assert_equal "2", redis.get("foo")
55
-
56
- rescue NotImplementedError => error
57
- raise unless error.message =~ /fork is not available/
58
- end
59
- end
60
- end
data/test/helper.rb DELETED
@@ -1,179 +0,0 @@
1
- require "test/unit"
2
- require "logger"
3
- require "stringio"
4
-
5
- $VERBOSE = true
6
-
7
- ENV["DRIVER"] ||= "ruby"
8
-
9
- require_relative "../lib/redis"
10
- require_relative "../lib/redis/connection/#{ENV["DRIVER"]}"
11
-
12
- require_relative "support/redis_mock"
13
- require_relative "support/connection/#{ENV["DRIVER"]}"
14
-
15
- PORT = 6381
16
- OPTIONS = {:port => PORT, :db => 15, :timeout => Float(ENV["TIMEOUT"] || 0.1)}
17
- NODES = ["redis://127.0.0.1:#{PORT}/15"]
18
-
19
- def init(redis)
20
- begin
21
- redis.select 14
22
- redis.flushdb
23
- redis.select 15
24
- redis.flushdb
25
- redis
26
- rescue Redis::CannotConnectError
27
- puts <<-EOS
28
-
29
- Cannot connect to Redis.
30
-
31
- Make sure Redis is running on localhost, port #{PORT}.
32
- This testing suite connects to the database 15.
33
-
34
- Try this once:
35
-
36
- $ make clean
37
-
38
- Then run the build again:
39
-
40
- $ make
41
-
42
- EOS
43
- exit 1
44
- end
45
- end
46
-
47
- def driver(*drivers, &blk)
48
- if drivers.map(&:to_s).include?(ENV["DRIVER"])
49
- class_eval(&blk)
50
- end
51
- end
52
-
53
- module Helper
54
-
55
- def run(runner)
56
- if respond_to?(:around)
57
- around { super(runner) }
58
- else
59
- super
60
- end
61
- end
62
-
63
- def silent
64
- verbose, $VERBOSE = $VERBOSE, false
65
-
66
- begin
67
- yield
68
- ensure
69
- $VERBOSE = verbose
70
- end
71
- end
72
-
73
- def with_external_encoding(encoding)
74
- original_encoding = Encoding.default_external
75
-
76
- begin
77
- silent { Encoding.default_external = Encoding.find(encoding) }
78
- yield
79
- ensure
80
- silent { Encoding.default_external = original_encoding }
81
- end
82
- end
83
-
84
- class Version
85
-
86
- include Comparable
87
-
88
- attr :parts
89
-
90
- def initialize(v)
91
- case v
92
- when Version
93
- @parts = v.parts
94
- else
95
- @parts = v.to_s.split(".")
96
- end
97
- end
98
-
99
- def <=>(other)
100
- other = Version.new(other)
101
- length = [self.parts.length, other.parts.length].max
102
- length.times do |i|
103
- a, b = self.parts[i], other.parts[i]
104
-
105
- return -1 if a.nil?
106
- return +1 if b.nil?
107
- return a.to_i <=> b.to_i if a != b
108
- end
109
-
110
- 0
111
- end
112
- end
113
-
114
- module Generic
115
-
116
- include Helper
117
-
118
- attr_reader :log
119
- attr_reader :redis
120
-
121
- alias :r :redis
122
-
123
- def setup
124
- @log = StringIO.new
125
- @redis = init _new_client
126
-
127
- # Run GC to make sure orphaned connections are closed.
128
- GC.start
129
- end
130
-
131
- def teardown
132
- @redis.quit if @redis
133
- end
134
-
135
- def redis_mock(commands, options = {}, &blk)
136
- RedisMock.start(commands, options) do |port|
137
- yield _new_client(options.merge(:port => port))
138
- end
139
- end
140
-
141
- def redis_mock_with_handler(handler, options = {}, &blk)
142
- RedisMock.start_with_handler(handler, options) do |port|
143
- yield _new_client(options.merge(:port => port))
144
- end
145
- end
146
-
147
- def assert_in_range(range, value)
148
- assert range.include?(value), "expected #{value} to be in #{range.inspect}"
149
- end
150
-
151
- def target_version(target)
152
- if version < target
153
- skip("Requires Redis > #{target}") if respond_to?(:skip)
154
- else
155
- yield
156
- end
157
- end
158
- end
159
-
160
- module Client
161
-
162
- include Generic
163
-
164
- def version
165
- Version.new(redis.info["redis_version"])
166
- end
167
-
168
- private
169
-
170
- def _format_options(options)
171
- OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
172
- end
173
-
174
- def _new_client(options = {})
175
- Redis.new(_format_options(options).merge(:driver => ENV["DRIVER"]))
176
- end
177
- end
178
-
179
- end
data/test/helper_test.rb DELETED
@@ -1,22 +0,0 @@
1
- require_relative "helper"
2
-
3
- class TestHelper < Test::Unit::TestCase
4
-
5
- include Helper
6
-
7
- def test_version_comparison
8
- v = Version.new("2.0.1")
9
-
10
- assert v > "1"
11
- assert v > "2"
12
- assert v < "3"
13
- assert v < "10"
14
-
15
- assert v < "2.1"
16
- assert v < "2.0.2"
17
- assert v < "2.0.1.1"
18
- assert v < "2.0.10"
19
-
20
- assert v == "2.0.1"
21
- end
22
- end