discourse-redis 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +59 -0
  4. data/.travis/Gemfile +11 -0
  5. data/.yardopts +3 -0
  6. data/CHANGELOG.md +349 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +328 -0
  10. data/Rakefile +87 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/consistency.rb +114 -0
  18. data/examples/dist_redis.rb +43 -0
  19. data/examples/incr-decr.rb +17 -0
  20. data/examples/list.rb +26 -0
  21. data/examples/pubsub.rb +37 -0
  22. data/examples/sentinel.rb +41 -0
  23. data/examples/sentinel/start +49 -0
  24. data/examples/sets.rb +36 -0
  25. data/examples/unicorn/config.ru +3 -0
  26. data/examples/unicorn/unicorn.rb +20 -0
  27. data/lib/redis.rb +2731 -0
  28. data/lib/redis/client.rb +575 -0
  29. data/lib/redis/connection.rb +9 -0
  30. data/lib/redis/connection/command_helper.rb +44 -0
  31. data/lib/redis/connection/hiredis.rb +64 -0
  32. data/lib/redis/connection/registry.rb +12 -0
  33. data/lib/redis/connection/ruby.rb +322 -0
  34. data/lib/redis/connection/synchrony.rb +124 -0
  35. data/lib/redis/distributed.rb +873 -0
  36. data/lib/redis/errors.rb +40 -0
  37. data/lib/redis/hash_ring.rb +132 -0
  38. data/lib/redis/pipeline.rb +141 -0
  39. data/lib/redis/subscribe.rb +83 -0
  40. data/lib/redis/version.rb +3 -0
  41. data/redis.gemspec +34 -0
  42. data/test/bitpos_test.rb +69 -0
  43. data/test/blocking_commands_test.rb +42 -0
  44. data/test/command_map_test.rb +30 -0
  45. data/test/commands_on_hashes_test.rb +21 -0
  46. data/test/commands_on_hyper_log_log_test.rb +21 -0
  47. data/test/commands_on_lists_test.rb +20 -0
  48. data/test/commands_on_sets_test.rb +77 -0
  49. data/test/commands_on_sorted_sets_test.rb +137 -0
  50. data/test/commands_on_strings_test.rb +101 -0
  51. data/test/commands_on_value_types_test.rb +133 -0
  52. data/test/connection_handling_test.rb +250 -0
  53. data/test/distributed_blocking_commands_test.rb +46 -0
  54. data/test/distributed_commands_on_hashes_test.rb +10 -0
  55. data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
  56. data/test/distributed_commands_on_lists_test.rb +22 -0
  57. data/test/distributed_commands_on_sets_test.rb +83 -0
  58. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  59. data/test/distributed_commands_on_strings_test.rb +59 -0
  60. data/test/distributed_commands_on_value_types_test.rb +95 -0
  61. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  62. data/test/distributed_connection_handling_test.rb +23 -0
  63. data/test/distributed_internals_test.rb +79 -0
  64. data/test/distributed_key_tags_test.rb +52 -0
  65. data/test/distributed_persistence_control_commands_test.rb +26 -0
  66. data/test/distributed_publish_subscribe_test.rb +92 -0
  67. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  68. data/test/distributed_scripting_test.rb +102 -0
  69. data/test/distributed_sorting_test.rb +20 -0
  70. data/test/distributed_test.rb +58 -0
  71. data/test/distributed_transactions_test.rb +32 -0
  72. data/test/encoding_test.rb +18 -0
  73. data/test/error_replies_test.rb +59 -0
  74. data/test/fork_safety_test.rb +65 -0
  75. data/test/helper.rb +232 -0
  76. data/test/helper_test.rb +24 -0
  77. data/test/internals_test.rb +437 -0
  78. data/test/lint/blocking_commands.rb +150 -0
  79. data/test/lint/hashes.rb +162 -0
  80. data/test/lint/hyper_log_log.rb +60 -0
  81. data/test/lint/lists.rb +143 -0
  82. data/test/lint/sets.rb +125 -0
  83. data/test/lint/sorted_sets.rb +316 -0
  84. data/test/lint/strings.rb +260 -0
  85. data/test/lint/value_types.rb +122 -0
  86. data/test/persistence_control_commands_test.rb +26 -0
  87. data/test/pipelining_commands_test.rb +242 -0
  88. data/test/publish_subscribe_test.rb +254 -0
  89. data/test/remote_server_control_commands_test.rb +118 -0
  90. data/test/scanning_test.rb +413 -0
  91. data/test/scripting_test.rb +78 -0
  92. data/test/sentinel_command_test.rb +80 -0
  93. data/test/sentinel_test.rb +255 -0
  94. data/test/sorting_test.rb +59 -0
  95. data/test/support/connection/hiredis.rb +1 -0
  96. data/test/support/connection/ruby.rb +1 -0
  97. data/test/support/connection/synchrony.rb +17 -0
  98. data/test/support/redis_mock.rb +119 -0
  99. data/test/support/wire/synchrony.rb +24 -0
  100. data/test/support/wire/thread.rb +5 -0
  101. data/test/synchrony_driver.rb +88 -0
  102. data/test/test.conf.erb +9 -0
  103. data/test/thread_safety_test.rb +32 -0
  104. data/test/transactions_test.rb +264 -0
  105. data/test/unknown_commands_test.rb +14 -0
  106. data/test/url_param_test.rb +138 -0
  107. metadata +182 -0
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestScripting < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def to_sha(script)
10
+ r.script(:load, script)
11
+ end
12
+
13
+ def test_script_exists
14
+ target_version "2.5.9" do # 2.6-rc1
15
+ a = to_sha("return 1")
16
+ b = a.succ
17
+
18
+ assert_equal true, r.script(:exists, a)
19
+ assert_equal false, r.script(:exists, b)
20
+ assert_equal [true], r.script(:exists, [a])
21
+ assert_equal [false], r.script(:exists, [b])
22
+ assert_equal [true, false], r.script(:exists, [a, b])
23
+ end
24
+ end
25
+
26
+ def test_script_flush
27
+ target_version "2.5.9" do # 2.6-rc1
28
+ sha = to_sha("return 1")
29
+ assert r.script(:exists, sha)
30
+ assert_equal "OK", r.script(:flush)
31
+ assert !r.script(:exists, sha)
32
+ end
33
+ end
34
+
35
+ def test_script_kill
36
+ target_version "2.5.9" do # 2.6-rc1
37
+ redis_mock(:script => lambda { |arg| "+#{arg.upcase}" }) do |redis|
38
+ assert_equal "KILL", redis.script(:kill)
39
+ end
40
+ end
41
+ end
42
+
43
+ def test_eval
44
+ target_version "2.5.9" do # 2.6-rc1
45
+ assert_equal 0, r.eval("return #KEYS")
46
+ assert_equal 0, r.eval("return #ARGV")
47
+ assert_equal ["k1", "k2"], r.eval("return KEYS", ["k1", "k2"])
48
+ assert_equal ["a1", "a2"], r.eval("return ARGV", [], ["a1", "a2"])
49
+ end
50
+ end
51
+
52
+ def test_eval_with_options_hash
53
+ target_version "2.5.9" do # 2.6-rc1
54
+ assert_equal 0, r.eval("return #KEYS", {})
55
+ assert_equal 0, r.eval("return #ARGV", {})
56
+ assert_equal ["k1", "k2"], r.eval("return KEYS", { :keys => ["k1", "k2"] })
57
+ assert_equal ["a1", "a2"], r.eval("return ARGV", { :argv => ["a1", "a2"] })
58
+ end
59
+ end
60
+
61
+ def test_evalsha
62
+ target_version "2.5.9" do # 2.6-rc1
63
+ assert_equal 0, r.evalsha(to_sha("return #KEYS"))
64
+ assert_equal 0, r.evalsha(to_sha("return #ARGV"))
65
+ assert_equal ["k1", "k2"], r.evalsha(to_sha("return KEYS"), ["k1", "k2"])
66
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), [], ["a1", "a2"])
67
+ end
68
+ end
69
+
70
+ def test_evalsha_with_options_hash
71
+ target_version "2.5.9" do # 2.6-rc1
72
+ assert_equal 0, r.evalsha(to_sha("return #KEYS"), {})
73
+ assert_equal 0, r.evalsha(to_sha("return #ARGV"), {})
74
+ assert_equal ["k1", "k2"], r.evalsha(to_sha("return KEYS"), { :keys => ["k1", "k2"] })
75
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), { :argv => ["a1", "a2"] })
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class SentinalCommandsTest < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_sentinel_command_master
10
+
11
+ handler = lambda do |id|
12
+ {
13
+ :sentinel => lambda do |command, *args|
14
+ ["name", "master1", "ip", "127.0.0.1"]
15
+ end
16
+ }
17
+ end
18
+
19
+ RedisMock.start(handler.call(:s1)) do |port|
20
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
21
+
22
+ result = redis.sentinel('master', 'master1')
23
+ assert_equal result, { "name" => "master1", "ip" => "127.0.0.1" }
24
+ end
25
+ end
26
+
27
+ def test_sentinel_command_masters
28
+
29
+ handler = lambda do |id|
30
+ {
31
+ :sentinel => lambda do |command, *args|
32
+ [%w[name master1 ip 127.0.0.1 port 6381], %w[name master1 ip 127.0.0.1 port 6382]]
33
+ end
34
+ }
35
+ end
36
+
37
+ RedisMock.start(handler.call(:s1)) do |port|
38
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
39
+
40
+ result = redis.sentinel('masters')
41
+ assert_equal result[0], { "name" => "master1", "ip" => "127.0.0.1", "port" => "6381" }
42
+ assert_equal result[1], { "name" => "master1", "ip" => "127.0.0.1", "port" => "6382" }
43
+ end
44
+ end
45
+
46
+ def test_sentinel_command_get_master_by_name
47
+
48
+ handler = lambda do |id|
49
+ {
50
+ :sentinel => lambda do |command, *args|
51
+ ["127.0.0.1", "6381"]
52
+ end
53
+ }
54
+ end
55
+
56
+ RedisMock.start(handler.call(:s1)) do |port|
57
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
58
+
59
+ result = redis.sentinel('get-master-addr-by-name', 'master1')
60
+ assert_equal result, ["127.0.0.1", "6381"]
61
+ end
62
+ end
63
+
64
+ def test_sentinel_command_ckquorum
65
+ handler = lambda do |id|
66
+ {
67
+ :sentinel => lambda do |command, *args|
68
+ "+OK 2 usable Sentinels. Quorum and failover authorization can be reached"
69
+ end
70
+ }
71
+ end
72
+
73
+ RedisMock.start(handler.call(:s1)) do |port|
74
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
75
+
76
+ result = redis.sentinel('ckquorum', 'master1')
77
+ assert_equal result, "OK 2 usable Sentinels. Quorum and failover authorization can be reached"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,255 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class SentinalTest < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_sentinel_connection
10
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
11
+ {:host => "127.0.0.1", :port => 26382}]
12
+
13
+ commands = {
14
+ :s1 => [],
15
+ :s2 => [],
16
+ }
17
+
18
+ handler = lambda do |id|
19
+ {
20
+ :sentinel => lambda do |command, *args|
21
+ commands[id] << [command, *args]
22
+ ["127.0.0.1", "6381"]
23
+ end
24
+ }
25
+ end
26
+
27
+ RedisMock.start(handler.call(:s1)) do |s1_port|
28
+ RedisMock.start(handler.call(:s2)) do |s2_port|
29
+ sentinels[0][:port] = s1_port
30
+ sentinels[1][:port] = s2_port
31
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
32
+
33
+ assert redis.ping
34
+ end
35
+ end
36
+
37
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
38
+ assert_equal commands[:s2], []
39
+ end
40
+
41
+ def test_sentinel_failover
42
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
43
+ {:host => "127.0.0.1", :port => 26382}]
44
+
45
+ commands = {
46
+ :s1 => [],
47
+ :s2 => [],
48
+ }
49
+
50
+ s1 = {
51
+ :sentinel => lambda do |command, *args|
52
+ commands[:s1] << [command, *args]
53
+ "$-1" # Nil
54
+ end
55
+ }
56
+
57
+ s2 = {
58
+ :sentinel => lambda do |command, *args|
59
+ commands[:s2] << [command, *args]
60
+ ["127.0.0.1", "6381"]
61
+ end
62
+ }
63
+
64
+ RedisMock.start(s1) do |s1_port|
65
+ RedisMock.start(s2) do |s2_port|
66
+ sentinels[0][:port] = s1_port
67
+ sentinels[1][:port] = s2_port
68
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
69
+
70
+ assert redis.ping
71
+ end
72
+ end
73
+
74
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
75
+ assert_equal commands[:s2], [%w[get-master-addr-by-name master1]]
76
+ end
77
+
78
+ def test_sentinel_failover_prioritize_healthy_sentinel
79
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
80
+ {:host => "127.0.0.1", :port => 26382}]
81
+
82
+ commands = {
83
+ :s1 => [],
84
+ :s2 => [],
85
+ }
86
+
87
+ s1 = {
88
+ :sentinel => lambda do |command, *args|
89
+ commands[:s1] << [command, *args]
90
+ "$-1" # Nil
91
+ end
92
+ }
93
+
94
+ s2 = {
95
+ :sentinel => lambda do |command, *args|
96
+ commands[:s2] << [command, *args]
97
+ ["127.0.0.1", "6381"]
98
+ end
99
+ }
100
+
101
+ RedisMock.start(s1) do |s1_port|
102
+ RedisMock.start(s2) do |s2_port|
103
+ sentinels[0][:port] = s1_port
104
+ sentinels[1][:port] = s2_port
105
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
106
+
107
+ assert redis.ping
108
+
109
+ redis.quit
110
+
111
+ assert redis.ping
112
+ end
113
+ end
114
+
115
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
116
+ assert_equal commands[:s2], [%w[get-master-addr-by-name master1], %w[get-master-addr-by-name master1]]
117
+ end
118
+
119
+ def test_sentinel_with_non_sentinel_options
120
+ sentinels = [{:host => "127.0.0.1", :port => 26381}]
121
+
122
+ commands = {
123
+ :s1 => [],
124
+ :m1 => []
125
+ }
126
+
127
+ sentinel = lambda do |port|
128
+ {
129
+ :auth => lambda do |pass|
130
+ commands[:s1] << ["auth", pass]
131
+ "-ERR unknown command 'auth'"
132
+ end,
133
+ :select => lambda do |db|
134
+ commands[:s1] << ["select", db]
135
+ "-ERR unknown command 'select'"
136
+ end,
137
+ :sentinel => lambda do |command, *args|
138
+ commands[:s1] << [command, *args]
139
+ ["127.0.0.1", port.to_s]
140
+ end
141
+ }
142
+ end
143
+
144
+ master = {
145
+ :auth => lambda do |pass|
146
+ commands[:m1] << ["auth", pass]
147
+ "+OK"
148
+ end,
149
+ :role => lambda do
150
+ commands[:m1] << ["role"]
151
+ ["master"]
152
+ end
153
+ }
154
+
155
+ RedisMock.start(master) do |master_port|
156
+ RedisMock.start(sentinel.call(master_port)) do |sen_port|
157
+ sentinels[0][:port] = sen_port
158
+ redis = Redis.new(:url => "redis://:foo@master1/15", :sentinels => sentinels, :role => :master)
159
+
160
+ assert redis.ping
161
+ end
162
+ end
163
+
164
+ assert_equal [%w[get-master-addr-by-name master1]], commands[:s1]
165
+ assert_equal [%w[auth foo], %w[role]], commands[:m1]
166
+ end
167
+
168
+ def test_sentinel_role_mismatch
169
+ sentinels = [{:host => "127.0.0.1", :port => 26381}]
170
+
171
+ sentinel = lambda do |port|
172
+ {
173
+ :sentinel => lambda do |command, *args|
174
+ ["127.0.0.1", port.to_s]
175
+ end
176
+ }
177
+ end
178
+
179
+ master = {
180
+ :role => lambda do
181
+ ["slave"]
182
+ end
183
+ }
184
+
185
+ ex = assert_raise(Redis::ConnectionError) do
186
+ RedisMock.start(master) do |master_port|
187
+ RedisMock.start(sentinel.call(master_port)) do |sen_port|
188
+ sentinels[0][:port] = sen_port
189
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
190
+
191
+ assert redis.ping
192
+ end
193
+ end
194
+ end
195
+
196
+ assert_match(/Instance role mismatch/, ex.message)
197
+ end
198
+
199
+ def test_sentinel_retries
200
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
201
+ {:host => "127.0.0.1", :port => 26382}]
202
+
203
+ connections = []
204
+
205
+ handler = lambda do |id, port|
206
+ {
207
+ :sentinel => lambda do |command, *args|
208
+ connections << id
209
+
210
+ if connections.count(id) < 2
211
+ :close
212
+ else
213
+ ["127.0.0.1", port.to_s]
214
+ end
215
+ end
216
+ }
217
+ end
218
+
219
+ master = {
220
+ :role => lambda do
221
+ ["master"]
222
+ end
223
+ }
224
+
225
+ RedisMock.start(master) do |master_port|
226
+ RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
227
+ RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
228
+ sentinels[0][:port] = s1_port
229
+ sentinels[1][:port] = s2_port
230
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 1)
231
+
232
+ assert redis.ping
233
+ end
234
+ end
235
+ end
236
+
237
+ assert_equal [:s1, :s2, :s1], connections
238
+
239
+ connections.clear
240
+
241
+ ex = assert_raise(Redis::CannotConnectError) do
242
+ RedisMock.start(master) do |master_port|
243
+ RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
244
+ RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
245
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 0)
246
+
247
+ assert redis.ping
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ assert_match(/No sentinels available/, ex.message)
254
+ end
255
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestSorting < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_sort
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_equal ["s1"], r.sort("bar", :get => "foo:*", :limit => [0, 1])
17
+ assert_equal ["s2"], r.sort("bar", :get => "foo:*", :limit => [0, 1], :order => "desc alpha")
18
+ end
19
+
20
+ def test_sort_with_an_array_of_gets
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_equal [["s1a", "s1b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1])
31
+ assert_equal [["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1], :order => "desc alpha")
32
+ assert_equal [["s1a", "s1b"], ["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"])
33
+ end
34
+
35
+ def test_sort_with_store
36
+ r.set("foo:1", "s1")
37
+ r.set("foo:2", "s2")
38
+
39
+ r.rpush("bar", "1")
40
+ r.rpush("bar", "2")
41
+
42
+ r.sort("bar", :get => "foo:*", :store => "baz")
43
+ assert_equal ["s1", "s2"], r.lrange("baz", 0, -1)
44
+ end
45
+
46
+ def test_sort_with_an_array_of_gets_and_with_store
47
+ r.set("foo:1:a", "s1a")
48
+ r.set("foo:1:b", "s1b")
49
+
50
+ r.set("foo:2:a", "s2a")
51
+ r.set("foo:2:b", "s2b")
52
+
53
+ r.rpush("bar", "1")
54
+ r.rpush("bar", "2")
55
+
56
+ r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :store => 'baz')
57
+ assert_equal ["s1a", "s1b", "s2a", "s2b"], r.lrange("baz", 0, -1)
58
+ end
59
+ end