discourse-redis 3.2.2

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