yam-redis-with-retries 2.2.2.1
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.
- data/CHANGELOG.md +53 -0
- data/LICENSE +20 -0
- data/README.md +208 -0
- data/Rakefile +277 -0
- data/benchmarking/logging.rb +62 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/thread_safety.rb +38 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/dist_redis.rb +43 -0
- data/examples/incr-decr.rb +17 -0
- data/examples/list.rb +26 -0
- data/examples/pubsub.rb +31 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis.rb +1166 -0
- data/lib/redis/client.rb +265 -0
- data/lib/redis/compat.rb +21 -0
- data/lib/redis/connection.rb +9 -0
- data/lib/redis/connection/command_helper.rb +45 -0
- data/lib/redis/connection/hiredis.rb +49 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +135 -0
- data/lib/redis/connection/synchrony.rb +129 -0
- data/lib/redis/distributed.rb +694 -0
- data/lib/redis/hash_ring.rb +131 -0
- data/lib/redis/pipeline.rb +34 -0
- data/lib/redis/retry.rb +128 -0
- data/lib/redis/subscribe.rb +94 -0
- data/lib/redis/version.rb +3 -0
- data/test/commands_on_hashes_test.rb +20 -0
- data/test/commands_on_lists_test.rb +60 -0
- data/test/commands_on_sets_test.rb +78 -0
- data/test/commands_on_sorted_sets_test.rb +109 -0
- data/test/commands_on_strings_test.rb +80 -0
- data/test/commands_on_value_types_test.rb +88 -0
- data/test/connection_handling_test.rb +88 -0
- data/test/distributed_blocking_commands_test.rb +53 -0
- data/test/distributed_commands_on_hashes_test.rb +12 -0
- data/test/distributed_commands_on_lists_test.rb +24 -0
- data/test/distributed_commands_on_sets_test.rb +85 -0
- data/test/distributed_commands_on_strings_test.rb +50 -0
- data/test/distributed_commands_on_value_types_test.rb +73 -0
- data/test/distributed_commands_requiring_clustering_test.rb +148 -0
- data/test/distributed_connection_handling_test.rb +25 -0
- data/test/distributed_internals_test.rb +27 -0
- data/test/distributed_key_tags_test.rb +53 -0
- data/test/distributed_persistence_control_commands_test.rb +24 -0
- data/test/distributed_publish_subscribe_test.rb +101 -0
- data/test/distributed_remote_server_control_commands_test.rb +43 -0
- data/test/distributed_sorting_test.rb +21 -0
- data/test/distributed_test.rb +59 -0
- data/test/distributed_transactions_test.rb +34 -0
- data/test/encoding_test.rb +16 -0
- data/test/error_replies_test.rb +53 -0
- data/test/helper.rb +145 -0
- data/test/internals_test.rb +163 -0
- data/test/lint/hashes.rb +126 -0
- data/test/lint/internals.rb +37 -0
- data/test/lint/lists.rb +93 -0
- data/test/lint/sets.rb +66 -0
- data/test/lint/sorted_sets.rb +167 -0
- data/test/lint/strings.rb +137 -0
- data/test/lint/value_types.rb +84 -0
- data/test/persistence_control_commands_test.rb +22 -0
- data/test/pipelining_commands_test.rb +123 -0
- data/test/publish_subscribe_test.rb +158 -0
- data/test/redis_mock.rb +80 -0
- data/test/remote_server_control_commands_test.rb +82 -0
- data/test/retry_test.rb +225 -0
- data/test/sorting_test.rb +44 -0
- data/test/synchrony_driver.rb +57 -0
- data/test/test.conf +8 -0
- data/test/thread_safety_test.rb +30 -0
- data/test/transactions_test.rb +100 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +60 -0
- metadata +215 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
+
require File.expand_path("./redis_mock", File.dirname(__FILE__))
|
5
|
+
|
6
|
+
include RedisMock::Helper
|
7
|
+
|
8
|
+
setup do
|
9
|
+
init Redis.new(OPTIONS)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "INFO" do |r|
|
13
|
+
%w(last_save_time redis_version total_connections_received connected_clients total_commands_processed connected_slaves uptime_in_seconds used_memory uptime_in_days changes_since_last_save).each do |x|
|
14
|
+
assert r.info.keys.include?(x)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "INFO COMMANDSTATS" do |r|
|
19
|
+
# Only available on Redis >= 2.3.0
|
20
|
+
next if r.info["redis_version"] < "2.3.0"
|
21
|
+
|
22
|
+
r.config(:resetstat)
|
23
|
+
r.ping
|
24
|
+
|
25
|
+
result = r.info(:commandstats)
|
26
|
+
assert "1" == result["ping"]["calls"]
|
27
|
+
end
|
28
|
+
|
29
|
+
test "MONITOR" do |r|
|
30
|
+
log = []
|
31
|
+
|
32
|
+
wire = Wire.new do
|
33
|
+
Redis.new(OPTIONS).monitor do |line|
|
34
|
+
log << line
|
35
|
+
break if log.size == 3
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Wire.pass while log.empty? # Faster than sleep
|
40
|
+
|
41
|
+
r.set "foo", "s1"
|
42
|
+
|
43
|
+
wire.join
|
44
|
+
|
45
|
+
assert log[-1][%q{(db 15) "set" "foo" "s1"}]
|
46
|
+
end
|
47
|
+
|
48
|
+
test "MONITOR returns value for break" do |r|
|
49
|
+
result = r.monitor do |line|
|
50
|
+
break line
|
51
|
+
end
|
52
|
+
|
53
|
+
assert result == "OK"
|
54
|
+
end
|
55
|
+
|
56
|
+
test "ECHO" do |r|
|
57
|
+
assert "foo bar baz\n" == r.echo("foo bar baz\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
test "DEBUG" do |r|
|
61
|
+
r.set "foo", "s1"
|
62
|
+
|
63
|
+
assert r.debug(:object, "foo").kind_of?(String)
|
64
|
+
end
|
65
|
+
|
66
|
+
test "OBJECT" do |r|
|
67
|
+
r.lpush "list", "value"
|
68
|
+
|
69
|
+
assert r.object(:refcount, "list") == 1
|
70
|
+
assert r.object(:encoding, "list") == "ziplist"
|
71
|
+
assert r.object(:idletime, "list").kind_of?(Fixnum)
|
72
|
+
end
|
73
|
+
|
74
|
+
test "SYNC" do |r|
|
75
|
+
replies = {:sync => lambda { "+OK" }}
|
76
|
+
|
77
|
+
redis_mock(replies) do
|
78
|
+
redis = Redis.new(OPTIONS.merge(:port => 6380))
|
79
|
+
|
80
|
+
assert "OK" == redis.sync
|
81
|
+
end
|
82
|
+
end
|
data/test/retry_test.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
+
require "mocha"
|
5
|
+
|
6
|
+
include Mocha::API
|
7
|
+
|
8
|
+
setup do
|
9
|
+
end
|
10
|
+
|
11
|
+
test "Happy path with retry enabled" do
|
12
|
+
client = Object.new
|
13
|
+
client.stubs(:call).with([:info]).returns("foo:bar")
|
14
|
+
Redis::Client.stubs(:new).returns(client)
|
15
|
+
|
16
|
+
redis = Redis.new(OPTIONS.merge({ :max_retries => 3 }))
|
17
|
+
|
18
|
+
assert_equal({ "foo" => "bar" }, redis.info)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "Method retried 1 number of times" do
|
22
|
+
client = mock()
|
23
|
+
client.stubs(:disconnect => nil)
|
24
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
25
|
+
Redis::Client.stubs(:new).returns(client)
|
26
|
+
|
27
|
+
redis = Redis.new(OPTIONS.merge({
|
28
|
+
:max_retries => 1,
|
29
|
+
:mark_dead => false
|
30
|
+
}))
|
31
|
+
|
32
|
+
begin
|
33
|
+
redis.info
|
34
|
+
raise "Should have reached max retries"
|
35
|
+
rescue Redis::Retry::MaxRetriesReachedError => e
|
36
|
+
assert_equal 1, e.retries
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
test "Method retried 5 number of times" do
|
41
|
+
client = mock()
|
42
|
+
client.stubs(:disconnect => nil)
|
43
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
44
|
+
Redis::Client.stubs(:new).returns(client)
|
45
|
+
|
46
|
+
redis = Redis.new(OPTIONS.merge({
|
47
|
+
:max_retries => 5,
|
48
|
+
:mark_dead => false
|
49
|
+
}))
|
50
|
+
|
51
|
+
begin
|
52
|
+
redis.info
|
53
|
+
raise "Should have reached max retries"
|
54
|
+
rescue Redis::Retry::MaxRetriesReachedError => e
|
55
|
+
assert_equal 5, e.retries
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
test "No retries if an unsupported exception is thrown" do
|
60
|
+
error = RuntimeError.new("foo")
|
61
|
+
client = mock()
|
62
|
+
client.stubs(:disconnect => nil)
|
63
|
+
client.stubs(:call).raises(error)
|
64
|
+
Redis::Client.stubs(:new).returns(client)
|
65
|
+
|
66
|
+
redis = Redis.new(OPTIONS.merge({ :max_retries => 1 }))
|
67
|
+
|
68
|
+
begin
|
69
|
+
redis.info
|
70
|
+
raise "Should have errored out"
|
71
|
+
rescue => e
|
72
|
+
assert_equal e, error
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
test "Test marked dead after retries exhausted" do
|
77
|
+
client = mock()
|
78
|
+
client.stubs(:disconnect => nil)
|
79
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
80
|
+
Redis::Client.stubs(:new).returns(client)
|
81
|
+
|
82
|
+
redis = Redis.new(OPTIONS.merge({
|
83
|
+
:max_retries => 3,
|
84
|
+
:mark_dead_seconds => 5
|
85
|
+
}))
|
86
|
+
|
87
|
+
begin
|
88
|
+
redis.info
|
89
|
+
raise "Should have been marked as dead"
|
90
|
+
rescue Redis::Retry::MarkedDeadError => e
|
91
|
+
# should still be dead.
|
92
|
+
client.stubs(:call).returns(:foo => :bar)
|
93
|
+
begin
|
94
|
+
redis.info
|
95
|
+
raise "Should still have been dead"
|
96
|
+
rescue Redis::Retry::MarkedDeadError => e
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
test "Test resuscitation after being marked as dead" do
|
102
|
+
client = mock()
|
103
|
+
client.stubs(:disconnect => nil)
|
104
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
105
|
+
Redis::Client.stubs(:new).returns(client)
|
106
|
+
|
107
|
+
redis = Redis.new(OPTIONS.merge({
|
108
|
+
:max_retries => 1,
|
109
|
+
:mark_dead_seconds => 1
|
110
|
+
}))
|
111
|
+
|
112
|
+
begin
|
113
|
+
redis.info
|
114
|
+
raise "Should have been marked as dead"
|
115
|
+
rescue Redis::Retry::MarkedDeadError => e
|
116
|
+
|
117
|
+
# should only be dead for 1 seconds
|
118
|
+
client.stubs(:call).returns(:foo => :bar)
|
119
|
+
sleep 1
|
120
|
+
|
121
|
+
assert_equal redis.info, {:foo => :bar}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
test "Test partial resuscitation" do
|
126
|
+
retries = 3
|
127
|
+
client = mock()
|
128
|
+
client.stubs(:disconnect => nil)
|
129
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
130
|
+
Redis::Client.stubs(:new).returns(client)
|
131
|
+
redis = Redis.new(OPTIONS.merge({
|
132
|
+
:max_retries => retries,
|
133
|
+
:mark_dead_seconds => 1
|
134
|
+
}))
|
135
|
+
|
136
|
+
begin
|
137
|
+
redis.info
|
138
|
+
raise "Should have been marked as dead"
|
139
|
+
rescue Redis::Retry::MarkedDeadError
|
140
|
+
sleep 1
|
141
|
+
end
|
142
|
+
|
143
|
+
## eh, gross, but handly for this test
|
144
|
+
class Redis; attr_accessor :retry; end
|
145
|
+
class Redis::Retry; attr_accessor :consecutive_errors; end
|
146
|
+
redis.retry.mark_alive!
|
147
|
+
|
148
|
+
assert_equal redis.retry.consecutive_errors, (retries - 1)
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
test "Should not retry if max_retries paramater is not specified" do
|
153
|
+
client = mock()
|
154
|
+
client.stubs(:disconnect => nil)
|
155
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
156
|
+
Redis::Client.stubs(:new).returns(client)
|
157
|
+
|
158
|
+
redis = Redis.new(OPTIONS)
|
159
|
+
|
160
|
+
begin
|
161
|
+
redis.info
|
162
|
+
raise "Should have failed"
|
163
|
+
rescue Errno::ECONNABORTED => e
|
164
|
+
# success
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
test "Should call on_retry callback" do
|
169
|
+
client = mock()
|
170
|
+
client.stubs(:disconnect => nil)
|
171
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
172
|
+
Redis::Client.stubs(:new).returns(client)
|
173
|
+
|
174
|
+
@ex = nil
|
175
|
+
redis = Redis.new(OPTIONS.merge({
|
176
|
+
:with_retry => true,
|
177
|
+
:on_retry => Proc.new{|e| assert e.kind_of?(Errno::ECONNABORTED); }
|
178
|
+
}))
|
179
|
+
|
180
|
+
begin
|
181
|
+
redis.info
|
182
|
+
rescue Redis::Retry::MarkedDeadError
|
183
|
+
## success
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
test "SHould call on_max_retries callback with mark_dead" do
|
188
|
+
client = mock()
|
189
|
+
client.stubs(:disconnect => nil)
|
190
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
191
|
+
Redis::Client.stubs(:new).returns(client)
|
192
|
+
|
193
|
+
@ex = nil
|
194
|
+
redis = Redis.new(OPTIONS.merge({
|
195
|
+
:with_retry => true,
|
196
|
+
:mark_dead => true,
|
197
|
+
:on_max_retries => Proc.new{|e| assert e.kind_of?(Redis::Retry::MarkedDeadError); }
|
198
|
+
}))
|
199
|
+
|
200
|
+
begin
|
201
|
+
redis.info
|
202
|
+
rescue Redis::Retry::MarkedDeadError
|
203
|
+
## success
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
test "Should call on_max_retries callback without mark_dead" do
|
208
|
+
client = mock()
|
209
|
+
client.stubs(:disconnect => nil)
|
210
|
+
client.stubs(:call).raises(Errno::ECONNABORTED)
|
211
|
+
Redis::Client.stubs(:new).returns(client)
|
212
|
+
|
213
|
+
@ex = nil
|
214
|
+
redis = Redis.new(OPTIONS.merge({
|
215
|
+
:with_retry => true,
|
216
|
+
:mark_dead => false,
|
217
|
+
:on_max_retries => Proc.new{|e| assert e.kind_of?(Redis::Retry::MaxRetriesReachedError); }
|
218
|
+
}))
|
219
|
+
|
220
|
+
begin
|
221
|
+
redis.info
|
222
|
+
rescue Redis::Retry::MaxRetriesReachedError
|
223
|
+
## success
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
setup do
|
6
|
+
init Redis.new(OPTIONS)
|
7
|
+
end
|
8
|
+
|
9
|
+
test "SORT" do |r|
|
10
|
+
r.set("foo:1", "s1")
|
11
|
+
r.set("foo:2", "s2")
|
12
|
+
|
13
|
+
r.rpush("bar", "1")
|
14
|
+
r.rpush("bar", "2")
|
15
|
+
|
16
|
+
assert ["s1"] == r.sort("bar", :get => "foo:*", :limit => [0, 1])
|
17
|
+
assert ["s2"] == r.sort("bar", :get => "foo:*", :limit => [0, 1], :order => "desc alpha")
|
18
|
+
end
|
19
|
+
|
20
|
+
test "SORT with an array of GETs" do |r|
|
21
|
+
r.set("foo:1:a", "s1a")
|
22
|
+
r.set("foo:1:b", "s1b")
|
23
|
+
|
24
|
+
r.set("foo:2:a", "s2a")
|
25
|
+
r.set("foo:2:b", "s2b")
|
26
|
+
|
27
|
+
r.rpush("bar", "1")
|
28
|
+
r.rpush("bar", "2")
|
29
|
+
|
30
|
+
assert ["s1a", "s1b"] == r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1])
|
31
|
+
assert ["s2a", "s2b"] == r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1], :order => "desc alpha")
|
32
|
+
end
|
33
|
+
|
34
|
+
test "SORT with STORE" do |r|
|
35
|
+
r.set("foo:1", "s1")
|
36
|
+
r.set("foo:2", "s2")
|
37
|
+
|
38
|
+
r.rpush("bar", "1")
|
39
|
+
r.rpush("bar", "2")
|
40
|
+
|
41
|
+
r.sort("bar", :get => "foo:*", :store => "baz")
|
42
|
+
assert ["s1", "s2"] == r.lrange("baz", 0, -1)
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'em-synchrony'
|
4
|
+
|
5
|
+
require 'redis'
|
6
|
+
require 'redis/connection/synchrony'
|
7
|
+
|
8
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
9
|
+
|
10
|
+
#
|
11
|
+
# if running under Eventmachine + Synchrony (Ruby 1.9+), then
|
12
|
+
# we can simulate the blocking API while performing the network
|
13
|
+
# IO via the EM reactor.
|
14
|
+
#
|
15
|
+
|
16
|
+
EM.synchrony do
|
17
|
+
r = Redis.new
|
18
|
+
r.flushdb
|
19
|
+
|
20
|
+
r.rpush "foo", "s1"
|
21
|
+
r.rpush "foo", "s2"
|
22
|
+
|
23
|
+
assert 2 == r.llen("foo")
|
24
|
+
assert "s2" == r.rpop("foo")
|
25
|
+
|
26
|
+
r.set("foo", "bar")
|
27
|
+
|
28
|
+
assert "bar" == r.getset("foo", "baz")
|
29
|
+
assert "baz" == r.get("foo")
|
30
|
+
|
31
|
+
r.set("foo", "a")
|
32
|
+
|
33
|
+
assert_equal 1, r.getbit("foo", 1)
|
34
|
+
assert_equal 1, r.getbit("foo", 2)
|
35
|
+
assert_equal 0, r.getbit("foo", 3)
|
36
|
+
assert_equal 0, r.getbit("foo", 4)
|
37
|
+
assert_equal 0, r.getbit("foo", 5)
|
38
|
+
assert_equal 0, r.getbit("foo", 6)
|
39
|
+
assert_equal 1, r.getbit("foo", 7)
|
40
|
+
|
41
|
+
r.flushdb
|
42
|
+
|
43
|
+
# command pipelining
|
44
|
+
r.pipelined do
|
45
|
+
r.lpush "foo", "s1"
|
46
|
+
r.lpush "foo", "s2"
|
47
|
+
end
|
48
|
+
|
49
|
+
assert 2 == r.llen("foo")
|
50
|
+
assert "s2" == r.lpop("foo")
|
51
|
+
assert "s1" == r.lpop("foo")
|
52
|
+
|
53
|
+
assert "OK" == r.client.call(:quit)
|
54
|
+
assert "PONG" == r.ping
|
55
|
+
|
56
|
+
EM.stop
|
57
|
+
end
|
data/test/test.conf
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
setup do
|
6
|
+
init Redis.new(OPTIONS)
|
7
|
+
end
|
8
|
+
|
9
|
+
test "thread safety" do
|
10
|
+
redis = Redis.connect(OPTIONS.merge(:thread_safe => true))
|
11
|
+
|
12
|
+
redis.set "foo", 1
|
13
|
+
redis.set "bar", 2
|
14
|
+
|
15
|
+
sample = 100
|
16
|
+
|
17
|
+
t1 = Thread.new do
|
18
|
+
$foos = Array.new(sample) { redis.get "foo" }
|
19
|
+
end
|
20
|
+
|
21
|
+
t2 = Thread.new do
|
22
|
+
$bars = Array.new(sample) { redis.get "bar" }
|
23
|
+
end
|
24
|
+
|
25
|
+
t1.join
|
26
|
+
t2.join
|
27
|
+
|
28
|
+
assert_equal ["1"], $foos.uniq
|
29
|
+
assert_equal ["2"], $bars.uniq
|
30
|
+
end
|