yam-redis-with-retries 2.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/CHANGELOG.md +53 -0
  2. data/LICENSE +20 -0
  3. data/README.md +208 -0
  4. data/Rakefile +277 -0
  5. data/benchmarking/logging.rb +62 -0
  6. data/benchmarking/pipeline.rb +51 -0
  7. data/benchmarking/speed.rb +21 -0
  8. data/benchmarking/suite.rb +24 -0
  9. data/benchmarking/thread_safety.rb +38 -0
  10. data/benchmarking/worker.rb +71 -0
  11. data/examples/basic.rb +15 -0
  12. data/examples/dist_redis.rb +43 -0
  13. data/examples/incr-decr.rb +17 -0
  14. data/examples/list.rb +26 -0
  15. data/examples/pubsub.rb +31 -0
  16. data/examples/sets.rb +36 -0
  17. data/examples/unicorn/config.ru +3 -0
  18. data/examples/unicorn/unicorn.rb +20 -0
  19. data/lib/redis.rb +1166 -0
  20. data/lib/redis/client.rb +265 -0
  21. data/lib/redis/compat.rb +21 -0
  22. data/lib/redis/connection.rb +9 -0
  23. data/lib/redis/connection/command_helper.rb +45 -0
  24. data/lib/redis/connection/hiredis.rb +49 -0
  25. data/lib/redis/connection/registry.rb +12 -0
  26. data/lib/redis/connection/ruby.rb +135 -0
  27. data/lib/redis/connection/synchrony.rb +129 -0
  28. data/lib/redis/distributed.rb +694 -0
  29. data/lib/redis/hash_ring.rb +131 -0
  30. data/lib/redis/pipeline.rb +34 -0
  31. data/lib/redis/retry.rb +128 -0
  32. data/lib/redis/subscribe.rb +94 -0
  33. data/lib/redis/version.rb +3 -0
  34. data/test/commands_on_hashes_test.rb +20 -0
  35. data/test/commands_on_lists_test.rb +60 -0
  36. data/test/commands_on_sets_test.rb +78 -0
  37. data/test/commands_on_sorted_sets_test.rb +109 -0
  38. data/test/commands_on_strings_test.rb +80 -0
  39. data/test/commands_on_value_types_test.rb +88 -0
  40. data/test/connection_handling_test.rb +88 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +163 -0
  61. data/test/lint/hashes.rb +126 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/retry_test.rb +225 -0
  74. data/test/sorting_test.rb +44 -0
  75. data/test/synchrony_driver.rb +57 -0
  76. data/test/test.conf +8 -0
  77. data/test/thread_safety_test.rb +30 -0
  78. data/test/transactions_test.rb +100 -0
  79. data/test/unknown_commands_test.rb +14 -0
  80. data/test/url_param_test.rb +60 -0
  81. 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
@@ -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,8 @@
1
+ dir ./test/db
2
+ pidfile ./redis.pid
3
+ port 6379
4
+ timeout 300
5
+ loglevel debug
6
+ logfile stdout
7
+ databases 16
8
+ daemonize yes
@@ -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