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.
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