redis 3.0.0.rc1 → 3.0.0.rc2

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 (77) hide show
  1. data/.travis.yml +50 -0
  2. data/.travis/Gemfile +11 -0
  3. data/CHANGELOG.md +47 -19
  4. data/README.md +160 -149
  5. data/Rakefile +15 -50
  6. data/examples/pubsub.rb +1 -1
  7. data/examples/unicorn/config.ru +1 -1
  8. data/examples/unicorn/unicorn.rb +1 -1
  9. data/lib/redis.rb +790 -390
  10. data/lib/redis/client.rb +137 -49
  11. data/lib/redis/connection/hiredis.rb +26 -15
  12. data/lib/redis/connection/ruby.rb +170 -53
  13. data/lib/redis/connection/synchrony.rb +23 -35
  14. data/lib/redis/distributed.rb +92 -32
  15. data/lib/redis/errors.rb +4 -2
  16. data/lib/redis/pipeline.rb +17 -6
  17. data/lib/redis/version.rb +1 -1
  18. data/redis.gemspec +4 -6
  19. data/test/blocking_commands_test.rb +42 -0
  20. data/test/command_map_test.rb +18 -17
  21. data/test/commands_on_hashes_test.rb +13 -12
  22. data/test/commands_on_lists_test.rb +35 -45
  23. data/test/commands_on_sets_test.rb +55 -54
  24. data/test/commands_on_sorted_sets_test.rb +106 -105
  25. data/test/commands_on_strings_test.rb +64 -55
  26. data/test/commands_on_value_types_test.rb +66 -54
  27. data/test/connection_handling_test.rb +136 -151
  28. data/test/distributed_blocking_commands_test.rb +33 -40
  29. data/test/distributed_commands_on_hashes_test.rb +6 -7
  30. data/test/distributed_commands_on_lists_test.rb +13 -14
  31. data/test/distributed_commands_on_sets_test.rb +57 -58
  32. data/test/distributed_commands_on_sorted_sets_test.rb +11 -12
  33. data/test/distributed_commands_on_strings_test.rb +31 -32
  34. data/test/distributed_commands_on_value_types_test.rb +61 -46
  35. data/test/distributed_commands_requiring_clustering_test.rb +108 -108
  36. data/test/distributed_connection_handling_test.rb +14 -15
  37. data/test/distributed_internals_test.rb +7 -19
  38. data/test/distributed_key_tags_test.rb +36 -36
  39. data/test/distributed_persistence_control_commands_test.rb +17 -14
  40. data/test/distributed_publish_subscribe_test.rb +61 -69
  41. data/test/distributed_remote_server_control_commands_test.rb +39 -28
  42. data/test/distributed_sorting_test.rb +12 -13
  43. data/test/distributed_test.rb +40 -41
  44. data/test/distributed_transactions_test.rb +20 -21
  45. data/test/encoding_test.rb +12 -9
  46. data/test/error_replies_test.rb +42 -36
  47. data/test/helper.rb +118 -85
  48. data/test/helper_test.rb +20 -6
  49. data/test/internals_test.rb +167 -103
  50. data/test/lint/blocking_commands.rb +124 -0
  51. data/test/lint/hashes.rb +115 -93
  52. data/test/lint/lists.rb +86 -80
  53. data/test/lint/sets.rb +68 -62
  54. data/test/lint/sorted_sets.rb +200 -195
  55. data/test/lint/strings.rb +112 -94
  56. data/test/lint/value_types.rb +76 -55
  57. data/test/persistence_control_commands_test.rb +17 -12
  58. data/test/pipelining_commands_test.rb +135 -126
  59. data/test/publish_subscribe_test.rb +105 -110
  60. data/test/remote_server_control_commands_test.rb +74 -58
  61. data/test/sorting_test.rb +31 -29
  62. data/test/support/connection/hiredis.rb +1 -0
  63. data/test/support/connection/ruby.rb +1 -0
  64. data/test/support/connection/synchrony.rb +17 -0
  65. data/test/{redis_mock.rb → support/redis_mock.rb} +24 -21
  66. data/test/support/wire/synchrony.rb +24 -0
  67. data/test/support/wire/thread.rb +5 -0
  68. data/test/synchrony_driver.rb +9 -9
  69. data/test/test.conf +1 -1
  70. data/test/thread_safety_test.rb +21 -19
  71. data/test/transactions_test.rb +189 -118
  72. data/test/unknown_commands_test.rb +9 -8
  73. data/test/url_param_test.rb +46 -41
  74. metadata +28 -43
  75. data/TODO.md +0 -4
  76. data/benchmarking/thread_safety.rb +0 -38
  77. data/test/lint/internals.rb +0 -36
@@ -1,158 +1,153 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require "helper"
4
4
 
5
- setup do
6
- init Redis.new(OPTIONS)
7
- end
5
+ class TestPublishSubscribe < Test::Unit::TestCase
8
6
 
9
- test "SUBSCRIBE and UNSUBSCRIBE" do |r|
10
- listening = false
7
+ include Helper::Client
11
8
 
12
- wire = Wire.new do
13
- r.subscribe("foo") do |on|
14
- on.subscribe do |channel, total|
15
- @subscribed = true
16
- @t1 = total
17
- end
9
+ def test_subscribe_and_unsubscribe
10
+ @subscribed = false
11
+ @unsubscribed = false
18
12
 
19
- on.message do |channel, message|
20
- if message == "s1"
21
- r.unsubscribe
22
- @message = message
13
+ wire = Wire.new do
14
+ r.subscribe("foo") do |on|
15
+ on.subscribe do |channel, total|
16
+ @subscribed = true
17
+ @t1 = total
23
18
  end
24
- end
25
19
 
26
- on.unsubscribe do |channel, total|
27
- @unsubscribed = true
28
- @t2 = total
29
- end
20
+ on.message do |channel, message|
21
+ if message == "s1"
22
+ r.unsubscribe
23
+ @message = message
24
+ end
25
+ end
30
26
 
31
- listening = true
27
+ on.unsubscribe do |channel, total|
28
+ @unsubscribed = true
29
+ @t2 = total
30
+ end
31
+ end
32
32
  end
33
- end
34
33
 
35
- Wire.pass while !listening
34
+ # Wait until the subscription is active before publishing
35
+ Wire.pass while !@subscribed
36
36
 
37
- Redis.new(OPTIONS).publish("foo", "s1")
37
+ Redis.new(OPTIONS).publish("foo", "s1")
38
38
 
39
- wire.join
39
+ wire.join
40
40
 
41
- assert @subscribed
42
- assert 1 == @t1
43
- assert @unsubscribed
44
- assert 0 == @t2
45
- assert "s1" == @message
46
- end
41
+ assert @subscribed
42
+ assert_equal 1, @t1
43
+ assert @unsubscribed
44
+ assert_equal 0, @t2
45
+ assert_equal "s1", @message
46
+ end
47
47
 
48
- test "PSUBSCRIBE and PUNSUBSCRIBE" do |r|
49
- listening = false
48
+ def test_psubscribe_and_punsubscribe
49
+ @subscribed = false
50
+ @unsubscribed = false
50
51
 
51
- wire = Wire.new do
52
- r.psubscribe("f*") do |on|
53
- on.psubscribe do |pattern, total|
54
- @subscribed = true
55
- @t1 = total
56
- end
52
+ wire = Wire.new do
53
+ r.psubscribe("f*") do |on|
54
+ on.psubscribe do |pattern, total|
55
+ @subscribed = true
56
+ @t1 = total
57
+ end
57
58
 
58
- on.pmessage do |pattern, channel, message|
59
- if message == "s1"
60
- r.punsubscribe
61
- @message = message
59
+ on.pmessage do |pattern, channel, message|
60
+ if message == "s1"
61
+ r.punsubscribe
62
+ @message = message
63
+ end
62
64
  end
63
- end
64
65
 
65
- on.punsubscribe do |pattern, total|
66
- @unsubscribed = true
67
- @t2 = total
68
- end
66
+ on.punsubscribe do |pattern, total|
67
+ @unsubscribed = true
68
+ @t2 = total
69
+ end
69
70
 
70
- listening = true
71
+ listening = true
72
+ end
71
73
  end
72
- end
73
74
 
74
- Wire.pass while !listening
75
+ # Wait until the subscription is active before publishing
76
+ Wire.pass while !@subscribed
75
77
 
76
- Redis.new(OPTIONS).publish("foo", "s1")
78
+ Redis.new(OPTIONS).publish("foo", "s1")
77
79
 
78
- wire.join
79
-
80
- assert @subscribed
81
- assert 1 == @t1
82
- assert @unsubscribed
83
- assert 0 == @t2
84
- assert "s1" == @message
85
- end
80
+ wire.join
86
81
 
87
- test "SUBSCRIBE within SUBSCRIBE" do |r|
88
- listening = false
82
+ assert @subscribed
83
+ assert_equal 1, @t1
84
+ assert @unsubscribed
85
+ assert_equal 0, @t2
86
+ assert_equal "s1", @message
87
+ end
89
88
 
90
- @channels = []
89
+ def test_subscribe_within_subscribe
90
+ @channels = []
91
91
 
92
- wire = Wire.new do
93
- r.subscribe("foo") do |on|
94
- on.subscribe do |channel, total|
95
- @channels << channel
92
+ wire = Wire.new do
93
+ r.subscribe("foo") do |on|
94
+ on.subscribe do |channel, total|
95
+ @channels << channel
96
96
 
97
- r.subscribe("bar") if channel == "foo"
98
- r.unsubscribe if channel == "bar"
97
+ r.subscribe("bar") if channel == "foo"
98
+ r.unsubscribe if channel == "bar"
99
+ end
99
100
  end
100
-
101
- listening = true
102
101
  end
103
- end
104
-
105
- Wire.pass while !listening
106
102
 
107
- Redis.new(OPTIONS).publish("foo", "s1")
103
+ wire.join
108
104
 
109
- wire.join
110
-
111
- assert ["foo", "bar"] == @channels
112
- end
105
+ assert_equal ["foo", "bar"], @channels
106
+ end
113
107
 
114
- test "other commands within a SUBSCRIBE" do |r|
115
- assert_raise Redis::CommandError do
116
- r.subscribe("foo") do |on|
117
- on.subscribe do |channel, total|
118
- r.set("bar", "s2")
108
+ def test_other_commands_within_a_subscribe
109
+ assert_raise Redis::CommandError do
110
+ r.subscribe("foo") do |on|
111
+ on.subscribe do |channel, total|
112
+ r.set("bar", "s2")
113
+ end
119
114
  end
120
115
  end
121
116
  end
122
- end
123
117
 
124
- test "SUBSCRIBE without a block" do |r|
125
- assert_raise LocalJumpError do
126
- r.subscribe("foo")
118
+ def test_subscribe_without_a_block
119
+ assert_raise LocalJumpError do
120
+ r.subscribe("foo")
121
+ end
127
122
  end
128
- end
129
123
 
130
- test "UNSUBSCRIBE without a SUBSCRIBE" do |r|
131
- assert_raise RuntimeError do
132
- r.unsubscribe
133
- end
124
+ def test_unsubscribe_without_a_subscribe
125
+ assert_raise RuntimeError do
126
+ r.unsubscribe
127
+ end
134
128
 
135
- assert_raise RuntimeError do
136
- r.punsubscribe
129
+ assert_raise RuntimeError do
130
+ r.punsubscribe
131
+ end
137
132
  end
138
- end
139
133
 
140
- test "SUBSCRIBE past a timeout" do |r|
141
- # For some reason, a thread here doesn't reproduce the issue.
142
- sleep = %{sleep #{OPTIONS[:timeout] + 1}}
143
- publish = %{echo "publish foo bar\r\n" | nc localhost #{OPTIONS[:port]}}
144
- cmd = [sleep, publish].join("; ")
134
+ def test_subscribe_past_a_timeout
135
+ # For some reason, a thread here doesn't reproduce the issue.
136
+ sleep = %{sleep #{OPTIONS[:timeout] * 2}}
137
+ publish = %{echo "publish foo bar\r\n" | nc localhost #{OPTIONS[:port]}}
138
+ cmd = [sleep, publish].join("; ")
145
139
 
146
- IO.popen(cmd, "r+") do |pipe|
147
- received = false
140
+ IO.popen(cmd, "r+") do |pipe|
141
+ received = false
148
142
 
149
- r.subscribe "foo" do |on|
150
- on.message do |channel, message|
151
- received = true
152
- r.unsubscribe
143
+ r.subscribe "foo" do |on|
144
+ on.message do |channel, message|
145
+ received = true
146
+ r.unsubscribe
147
+ end
153
148
  end
154
- end
155
149
 
156
- assert received
150
+ assert received
151
+ end
157
152
  end
158
153
  end
@@ -1,88 +1,104 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
- require File.expand_path("./redis_mock", File.dirname(__FILE__))
3
+ require "helper"
5
4
 
6
- include RedisMock::Helper
5
+ class TestRemoteServerControlCommands < Test::Unit::TestCase
7
6
 
8
- setup do
9
- init Redis.new(OPTIONS)
10
- end
7
+ include Helper::Client
11
8
 
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)
9
+ def test_info
10
+ %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|
11
+ assert r.info.keys.include?(x)
12
+ end
15
13
  end
16
- end
17
14
 
18
- test "INFO COMMANDSTATS" do |r|
19
- # Only available on Redis >= 2.9.0
20
- next if version(r) < 209000
15
+ def test_info_commandstats
16
+ return if version < "2.9.0"
21
17
 
22
- r.config(:resetstat)
23
- r.ping
18
+ r.config(:resetstat)
19
+ r.ping
24
20
 
25
- result = r.info(:commandstats)
26
- assert "1" == result["ping"]["calls"]
27
- end
21
+ result = r.info(:commandstats)
22
+ assert_equal "1", result["ping"]["calls"]
23
+ end
24
+
25
+ def test_monitor_redis_lt_2_5_0
26
+ return unless version < "2.5.0"
28
27
 
29
- test "MONITOR" do |r|
30
- log = []
28
+ log = []
31
29
 
32
- wire = Wire.new do
33
- Redis.new(OPTIONS).monitor do |line|
34
- log << line
35
- break if log.size == 3
30
+ wire = Wire.new do
31
+ Redis.new(OPTIONS).monitor do |line|
32
+ log << line
33
+ break if log.size == 3
34
+ end
36
35
  end
36
+
37
+ Wire.pass while log.empty? # Faster than sleep
38
+
39
+ r.set "foo", "s1"
40
+
41
+ wire.join
42
+
43
+ assert log[-1][%q{(db 15) "set" "foo" "s1"}]
37
44
  end
38
45
 
39
- Wire.pass while log.empty? # Faster than sleep
46
+ def test_monitor_redis_gte_2_5_0
47
+ return unless version >= "2.5.0"
40
48
 
41
- r.set "foo", "s1"
49
+ log = []
42
50
 
43
- wire.join
51
+ wire = Wire.new do
52
+ Redis.new(OPTIONS).monitor do |line|
53
+ log << line
54
+ break if line =~ /set/
55
+ end
56
+ end
44
57
 
45
- assert log[-1][%q{(db 15) "set" "foo" "s1"}]
46
- end
58
+ Wire.pass while log.empty? # Faster than sleep
47
59
 
48
- test "MONITOR returns value for break" do |r|
49
- result = r.monitor do |line|
50
- break line
51
- end
60
+ r.set "foo", "s1"
52
61
 
53
- assert result == "OK"
54
- end
62
+ wire.join
55
63
 
56
- test "ECHO" do |r|
57
- assert "foo bar baz\n" == r.echo("foo bar baz\n")
58
- end
64
+ assert log[-1] =~ /\b15\b.* "set" "foo" "s1"/
65
+ end
59
66
 
60
- test "DEBUG" do |r|
61
- r.set "foo", "s1"
67
+ def test_monitor_returns_value_for_break
68
+ result = r.monitor do |line|
69
+ break line
70
+ end
62
71
 
63
- assert r.debug(:object, "foo").kind_of?(String)
64
- end
72
+ assert_equal result, "OK"
73
+ end
65
74
 
66
- test "OBJECT" do |r|
67
- r.lpush "list", "value"
75
+ def test_echo
76
+ assert_equal "foo bar baz\n", r.echo("foo bar baz\n")
77
+ end
68
78
 
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
79
+ def test_debug
80
+ r.set "foo", "s1"
73
81
 
74
- test "SYNC" do |r|
75
- replies = {:sync => lambda { "+OK" }}
82
+ assert r.debug(:object, "foo").kind_of?(String)
83
+ end
76
84
 
77
- redis_mock(replies) do
78
- redis = Redis.new(OPTIONS.merge(:port => 6380))
85
+ def test_object
86
+ r.lpush "list", "value"
79
87
 
80
- assert "OK" == redis.sync
88
+ assert_equal r.object(:refcount, "list"), 1
89
+ assert_equal r.object(:encoding, "list"), "ziplist"
90
+ assert r.object(:idletime, "list").kind_of?(Fixnum)
91
+ end
92
+
93
+ def test_sync
94
+ redis_mock(:sync => lambda { "+OK" }) do |redis|
95
+ assert_equal "OK", redis.sync
96
+ end
81
97
  end
82
- end
83
98
 
84
- test "SLOWLOG" do |r|
85
- r.slowlog(:reset)
86
- result = r.slowlog(:len)
87
- assert result == 0
99
+ def test_slowlog
100
+ r.slowlog(:reset)
101
+ result = r.slowlog(:len)
102
+ assert_equal result, 0
103
+ end
88
104
  end
@@ -1,43 +1,45 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require "helper"
4
4
 
5
- setup do
6
- init Redis.new(OPTIONS)
7
- end
5
+ class TestSorting < Test::Unit::TestCase
8
6
 
9
- test "SORT" do |r|
10
- r.set("foo:1", "s1")
11
- r.set("foo:2", "s2")
7
+ include Helper::Client
12
8
 
13
- r.rpush("bar", "1")
14
- r.rpush("bar", "2")
9
+ def test_sort
10
+ r.set("foo:1", "s1")
11
+ r.set("foo:2", "s2")
15
12
 
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
13
+ r.rpush("bar", "1")
14
+ r.rpush("bar", "2")
19
15
 
20
- test "SORT with an array of GETs" do |r|
21
- r.set("foo:1:a", "s1a")
22
- r.set("foo:1:b", "s1b")
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
23
19
 
24
- r.set("foo:2:a", "s2a")
25
- r.set("foo:2:b", "s2b")
20
+ def test_sort_with_an_array_of_gets
21
+ r.set("foo:1:a", "s1a")
22
+ r.set("foo:1:b", "s1b")
26
23
 
27
- r.rpush("bar", "1")
28
- r.rpush("bar", "2")
24
+ r.set("foo:2:a", "s2a")
25
+ r.set("foo:2:b", "s2b")
29
26
 
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
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
33
34
 
34
- test "SORT with STORE" do |r|
35
- r.set("foo:1", "s1")
36
- r.set("foo:2", "s2")
35
+ def test_sort_with_store
36
+ r.set("foo:1", "s1")
37
+ r.set("foo:2", "s2")
37
38
 
38
- r.rpush("bar", "1")
39
- r.rpush("bar", "2")
39
+ r.rpush("bar", "1")
40
+ r.rpush("bar", "2")
40
41
 
41
- r.sort("bar", :get => "foo:*", :store => "baz")
42
- assert ["s1", "s2"] == r.lrange("baz", 0, -1)
42
+ r.sort("bar", :get => "foo:*", :store => "baz")
43
+ assert_equal ["s1", "s2"], r.lrange("baz", 0, -1)
44
+ end
43
45
  end