redis 3.3.5 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +132 -2
  3. data/README.md +144 -79
  4. data/lib/redis.rb +1174 -405
  5. data/lib/redis/client.rb +150 -90
  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 +93 -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 +6 -5
  19. data/lib/redis/connection/registry.rb +2 -1
  20. data/lib/redis/connection/ruby.rb +126 -128
  21. data/lib/redis/connection/synchrony.rb +21 -8
  22. data/lib/redis/distributed.rb +147 -72
  23. data/lib/redis/errors.rb +48 -0
  24. data/lib/redis/hash_ring.rb +30 -73
  25. data/lib/redis/pipeline.rb +55 -15
  26. data/lib/redis/subscribe.rb +11 -12
  27. data/lib/redis/version.rb +3 -1
  28. metadata +49 -202
  29. data/.gitignore +0 -16
  30. data/.travis.yml +0 -89
  31. data/.travis/Gemfile +0 -11
  32. data/.yardopts +0 -3
  33. data/Gemfile +0 -4
  34. data/Rakefile +0 -87
  35. data/benchmarking/logging.rb +0 -71
  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/consistency.rb +0 -114
  42. data/examples/dist_redis.rb +0 -43
  43. data/examples/incr-decr.rb +0 -17
  44. data/examples/list.rb +0 -26
  45. data/examples/pubsub.rb +0 -37
  46. data/examples/sentinel.rb +0 -41
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sets.rb +0 -36
  50. data/examples/unicorn/config.ru +0 -3
  51. data/examples/unicorn/unicorn.rb +0 -20
  52. data/redis.gemspec +0 -44
  53. data/test/bitpos_test.rb +0 -69
  54. data/test/blocking_commands_test.rb +0 -42
  55. data/test/client_test.rb +0 -59
  56. data/test/command_map_test.rb +0 -30
  57. data/test/commands_on_hashes_test.rb +0 -21
  58. data/test/commands_on_hyper_log_log_test.rb +0 -21
  59. data/test/commands_on_lists_test.rb +0 -20
  60. data/test/commands_on_sets_test.rb +0 -77
  61. data/test/commands_on_sorted_sets_test.rb +0 -137
  62. data/test/commands_on_strings_test.rb +0 -101
  63. data/test/commands_on_value_types_test.rb +0 -133
  64. data/test/connection_handling_test.rb +0 -277
  65. data/test/connection_test.rb +0 -57
  66. data/test/db/.gitkeep +0 -0
  67. data/test/distributed_blocking_commands_test.rb +0 -46
  68. data/test/distributed_commands_on_hashes_test.rb +0 -10
  69. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  70. data/test/distributed_commands_on_lists_test.rb +0 -22
  71. data/test/distributed_commands_on_sets_test.rb +0 -83
  72. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  73. data/test/distributed_commands_on_strings_test.rb +0 -59
  74. data/test/distributed_commands_on_value_types_test.rb +0 -95
  75. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  76. data/test/distributed_connection_handling_test.rb +0 -23
  77. data/test/distributed_internals_test.rb +0 -79
  78. data/test/distributed_key_tags_test.rb +0 -52
  79. data/test/distributed_persistence_control_commands_test.rb +0 -26
  80. data/test/distributed_publish_subscribe_test.rb +0 -92
  81. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  82. data/test/distributed_scripting_test.rb +0 -102
  83. data/test/distributed_sorting_test.rb +0 -20
  84. data/test/distributed_test.rb +0 -58
  85. data/test/distributed_transactions_test.rb +0 -32
  86. data/test/encoding_test.rb +0 -18
  87. data/test/error_replies_test.rb +0 -59
  88. data/test/fork_safety_test.rb +0 -65
  89. data/test/helper.rb +0 -232
  90. data/test/helper_test.rb +0 -24
  91. data/test/internals_test.rb +0 -417
  92. data/test/lint/blocking_commands.rb +0 -150
  93. data/test/lint/hashes.rb +0 -162
  94. data/test/lint/hyper_log_log.rb +0 -60
  95. data/test/lint/lists.rb +0 -143
  96. data/test/lint/sets.rb +0 -140
  97. data/test/lint/sorted_sets.rb +0 -316
  98. data/test/lint/strings.rb +0 -260
  99. data/test/lint/value_types.rb +0 -122
  100. data/test/persistence_control_commands_test.rb +0 -26
  101. data/test/pipelining_commands_test.rb +0 -242
  102. data/test/publish_subscribe_test.rb +0 -282
  103. data/test/remote_server_control_commands_test.rb +0 -118
  104. data/test/scanning_test.rb +0 -413
  105. data/test/scripting_test.rb +0 -78
  106. data/test/sentinel_command_test.rb +0 -80
  107. data/test/sentinel_test.rb +0 -255
  108. data/test/sorting_test.rb +0 -59
  109. data/test/ssl_test.rb +0 -73
  110. data/test/support/connection/hiredis.rb +0 -1
  111. data/test/support/connection/ruby.rb +0 -1
  112. data/test/support/connection/synchrony.rb +0 -17
  113. data/test/support/redis_mock.rb +0 -130
  114. data/test/support/ssl/gen_certs.sh +0 -31
  115. data/test/support/ssl/trusted-ca.crt +0 -25
  116. data/test/support/ssl/trusted-ca.key +0 -27
  117. data/test/support/ssl/trusted-cert.crt +0 -81
  118. data/test/support/ssl/trusted-cert.key +0 -28
  119. data/test/support/ssl/untrusted-ca.crt +0 -26
  120. data/test/support/ssl/untrusted-ca.key +0 -27
  121. data/test/support/ssl/untrusted-cert.crt +0 -82
  122. data/test/support/ssl/untrusted-cert.key +0 -28
  123. data/test/support/wire/synchrony.rb +0 -24
  124. data/test/support/wire/thread.rb +0 -5
  125. data/test/synchrony_driver.rb +0 -88
  126. data/test/test.conf.erb +0 -9
  127. data/test/thread_safety_test.rb +0 -62
  128. data/test/transactions_test.rb +0 -264
  129. data/test/unknown_commands_test.rb +0 -14
  130. data/test/url_param_test.rb +0 -138
@@ -1,20 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
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 File.expand_path("helper", File.dirname(__FILE__))
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://127.0.0.1:6380/14")
36
-
37
- assert_equal "127.0.0.1", @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 File.expand_path("helper", File.dirname(__FILE__))
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 File.expand_path("helper", File.dirname(__FILE__))
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 File.expand_path("helper", File.dirname(__FILE__))
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,65 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestForkSafety < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
- include Helper::Skipable
9
-
10
- driver(:ruby, :hiredis) do
11
- def test_fork_safety
12
- redis = Redis.new(OPTIONS)
13
- redis.set "foo", 1
14
-
15
- child_pid = fork do
16
- begin
17
- # InheritedError triggers a reconnect,
18
- # so we need to disable reconnects to force
19
- # the exception bubble up
20
- redis.without_reconnect do
21
- redis.set "foo", 2
22
- end
23
- rescue Redis::InheritedError
24
- exit 127
25
- end
26
- end
27
-
28
- _, status = Process.wait2(child_pid)
29
-
30
- assert_equal 127, status.exitstatus
31
- assert_equal "1", redis.get("foo")
32
-
33
- rescue NotImplementedError => error
34
- raise unless error.message =~ /fork is not available/
35
- return skip(error.message)
36
- end
37
-
38
- def test_fork_safety_with_enabled_inherited_socket
39
- redis = Redis.new(OPTIONS.merge(:inherit_socket => true))
40
- redis.set "foo", 1
41
-
42
- child_pid = fork do
43
- begin
44
- # InheritedError triggers a reconnect,
45
- # so we need to disable reconnects to force
46
- # the exception bubble up
47
- redis.without_reconnect do
48
- redis.set "foo", 2
49
- end
50
- rescue Redis::InheritedError
51
- exit 127
52
- end
53
- end
54
-
55
- _, status = Process.wait2(child_pid)
56
-
57
- assert_equal 0, status.exitstatus
58
- assert_equal "2", redis.get("foo")
59
-
60
- rescue NotImplementedError => error
61
- raise unless error.message =~ /fork is not available/
62
- return skip(error.message)
63
- end
64
- end
65
- end
data/test/helper.rb DELETED
@@ -1,232 +0,0 @@
1
- $:.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
- $:.unshift File.expand_path(File.dirname(__FILE__))
3
-
4
- require "test/unit"
5
- require "logger"
6
- require "stringio"
7
-
8
- (class Random; def self.rand(*args) super end; end) unless defined?(Random)
9
-
10
- begin
11
- require "ruby-debug"
12
- rescue LoadError
13
- end
14
-
15
- $VERBOSE = true
16
-
17
- ENV["conn"] ||= "ruby"
18
-
19
- require "redis"
20
- require "redis/distributed"
21
- require "redis/connection/#{ENV["conn"]}"
22
-
23
- require "support/redis_mock"
24
- require "support/connection/#{ENV["conn"]}"
25
-
26
- PORT = 6381
27
- OPTIONS = {:port => PORT, :db => 15, :timeout => Float(ENV["TIMEOUT"] || 0.1)}
28
- NODES = ["redis://127.0.0.1:#{PORT}/15"]
29
-
30
- def init(redis)
31
- begin
32
- redis.select 14
33
- redis.flushdb
34
- redis.select 15
35
- redis.flushdb
36
- redis
37
- rescue Redis::CannotConnectError
38
- puts <<-EOS
39
-
40
- Cannot connect to Redis.
41
-
42
- Make sure Redis is running on localhost, port #{PORT}.
43
- This testing suite connects to the database 15.
44
-
45
- Try this once:
46
-
47
- $ rake clean
48
-
49
- Then run the build again:
50
-
51
- $ rake
52
-
53
- EOS
54
- exit 1
55
- end
56
- end
57
-
58
- def driver(*drivers, &blk)
59
- if drivers.map(&:to_s).include?(ENV["conn"])
60
- class_eval(&blk)
61
- end
62
- end
63
-
64
- module Helper
65
-
66
- def run(runner)
67
- if respond_to?(:around)
68
- around { super(runner) }
69
- else
70
- super
71
- end
72
- end
73
-
74
- def silent
75
- verbose, $VERBOSE = $VERBOSE, false
76
-
77
- begin
78
- yield
79
- ensure
80
- $VERBOSE = verbose
81
- end
82
- end
83
-
84
- def with_external_encoding(encoding)
85
- original_encoding = Encoding.default_external
86
-
87
- begin
88
- silent { Encoding.default_external = Encoding.find(encoding) }
89
- yield
90
- ensure
91
- silent { Encoding.default_external = original_encoding }
92
- end
93
- end
94
-
95
- def try_encoding(encoding, &block)
96
- if defined?(Encoding)
97
- with_external_encoding(encoding, &block)
98
- else
99
- yield
100
- end
101
- end
102
-
103
- class Version
104
-
105
- include Comparable
106
-
107
- attr :parts
108
-
109
- def initialize(v)
110
- case v
111
- when Version
112
- @parts = v.parts
113
- else
114
- @parts = v.to_s.split(".")
115
- end
116
- end
117
-
118
- def <=>(other)
119
- other = Version.new(other)
120
- length = [self.parts.length, other.parts.length].max
121
- length.times do |i|
122
- a, b = self.parts[i], other.parts[i]
123
-
124
- return -1 if a.nil?
125
- return +1 if b.nil?
126
- return a.to_i <=> b.to_i if a != b
127
- end
128
-
129
- 0
130
- end
131
- end
132
-
133
- module Generic
134
-
135
- include Helper
136
-
137
- attr_reader :log
138
- attr_reader :redis
139
-
140
- alias :r :redis
141
-
142
- def setup
143
- @log = StringIO.new
144
- @redis = init _new_client
145
-
146
- # Run GC to make sure orphaned connections are closed.
147
- GC.start
148
- end
149
-
150
- def teardown
151
- @redis.quit if @redis
152
- end
153
-
154
- def redis_mock(commands, options = {}, &blk)
155
- RedisMock.start(commands, options) do |port|
156
- yield _new_client(options.merge(:port => port))
157
- end
158
- end
159
-
160
- def redis_mock_with_handler(handler, options = {}, &blk)
161
- RedisMock.start_with_handler(handler, options) do |port|
162
- yield _new_client(options.merge(:port => port))
163
- end
164
- end
165
-
166
- def assert_in_range(range, value)
167
- assert range.include?(value), "expected #{value} to be in #{range.inspect}"
168
- end
169
-
170
- def target_version(target)
171
- if version < target
172
- skip("Requires Redis > #{target}") if respond_to?(:skip)
173
- else
174
- yield
175
- end
176
- end
177
- end
178
-
179
- module Client
180
-
181
- include Generic
182
-
183
- def version
184
- Version.new(redis.info["redis_version"])
185
- end
186
-
187
- private
188
-
189
- def _format_options(options)
190
- OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
191
- end
192
-
193
- def _new_client(options = {})
194
- Redis.new(_format_options(options).merge(:driver => ENV["conn"]))
195
- end
196
- end
197
-
198
- module Distributed
199
-
200
- include Generic
201
-
202
- def version
203
- Version.new(redis.info.first["redis_version"])
204
- end
205
-
206
- private
207
-
208
- def _format_options(options)
209
- {
210
- :timeout => OPTIONS[:timeout],
211
- :logger => ::Logger.new(@log),
212
- }.merge(options)
213
- end
214
-
215
- def _new_client(options = {})
216
- Redis::Distributed.new(NODES, _format_options(options).merge(:driver => ENV["conn"]))
217
- end
218
- end
219
-
220
- # Basic support for `skip` in 1.8.x
221
- # Note: YOU MUST use `return skip(message)` in order to appropriately bail
222
- # from a running test.
223
- module Skipable
224
- Skipped = Class.new(RuntimeError)
225
-
226
- def skip(message = nil, bt = caller)
227
- return super if defined?(super)
228
-
229
- $stderr.puts("SKIPPED: #{self} #{message || 'no reason given'}")
230
- end
231
- end
232
- end