redis 2.1.1 → 2.2.0

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 (79) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +190 -0
  4. data/Rakefile +194 -79
  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 +612 -156
  20. data/lib/redis/client.rb +98 -57
  21. data/lib/redis/connection.rb +9 -134
  22. data/lib/redis/connection/command_helper.rb +45 -0
  23. data/lib/redis/connection/hiredis.rb +49 -0
  24. data/lib/redis/connection/registry.rb +12 -0
  25. data/lib/redis/connection/ruby.rb +131 -0
  26. data/lib/redis/connection/synchrony.rb +125 -0
  27. data/lib/redis/distributed.rb +161 -5
  28. data/lib/redis/pipeline.rb +6 -0
  29. data/lib/redis/version.rb +3 -0
  30. data/redis.gemspec +24 -0
  31. data/test/commands_on_hashes_test.rb +32 -0
  32. data/test/commands_on_lists_test.rb +60 -0
  33. data/test/commands_on_sets_test.rb +78 -0
  34. data/test/commands_on_sorted_sets_test.rb +109 -0
  35. data/test/commands_on_strings_test.rb +80 -0
  36. data/test/commands_on_value_types_test.rb +88 -0
  37. data/test/connection_handling_test.rb +87 -0
  38. data/test/db/.gitignore +1 -0
  39. data/test/distributed_blocking_commands_test.rb +53 -0
  40. data/test/distributed_commands_on_hashes_test.rb +12 -0
  41. data/test/distributed_commands_on_lists_test.rb +24 -0
  42. data/test/distributed_commands_on_sets_test.rb +85 -0
  43. data/test/distributed_commands_on_strings_test.rb +50 -0
  44. data/test/distributed_commands_on_value_types_test.rb +73 -0
  45. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  46. data/test/distributed_connection_handling_test.rb +25 -0
  47. data/test/distributed_internals_test.rb +18 -0
  48. data/test/distributed_key_tags_test.rb +53 -0
  49. data/test/distributed_persistence_control_commands_test.rb +24 -0
  50. data/test/distributed_publish_subscribe_test.rb +101 -0
  51. data/test/distributed_remote_server_control_commands_test.rb +31 -0
  52. data/test/distributed_sorting_test.rb +21 -0
  53. data/test/distributed_test.rb +60 -0
  54. data/test/distributed_transactions_test.rb +34 -0
  55. data/test/encoding_test.rb +16 -0
  56. data/test/error_replies_test.rb +53 -0
  57. data/test/helper.rb +145 -0
  58. data/test/internals_test.rb +157 -0
  59. data/test/lint/hashes.rb +114 -0
  60. data/test/lint/internals.rb +41 -0
  61. data/test/lint/lists.rb +93 -0
  62. data/test/lint/sets.rb +66 -0
  63. data/test/lint/sorted_sets.rb +167 -0
  64. data/test/lint/strings.rb +137 -0
  65. data/test/lint/value_types.rb +84 -0
  66. data/test/persistence_control_commands_test.rb +22 -0
  67. data/test/pipelining_commands_test.rb +123 -0
  68. data/test/publish_subscribe_test.rb +158 -0
  69. data/test/redis_mock.rb +80 -0
  70. data/test/remote_server_control_commands_test.rb +63 -0
  71. data/test/sorting_test.rb +44 -0
  72. data/test/synchrony_driver.rb +57 -0
  73. data/test/test.conf +8 -0
  74. data/test/thread_safety_test.rb +30 -0
  75. data/test/transactions_test.rb +100 -0
  76. data/test/unknown_commands_test.rb +14 -0
  77. data/test/url_param_test.rb +60 -0
  78. metadata +128 -19
  79. data/README.markdown +0 -129
@@ -0,0 +1,87 @@
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 "AUTH" do
13
+ replies = {
14
+ :auth => lambda { |password| $auth = password; "+OK" },
15
+ :get => lambda { |key| $auth == "secret" ? "$3\r\nbar" : "$-1" },
16
+ }
17
+
18
+ redis_mock(replies) do
19
+ redis = Redis.new(OPTIONS.merge(:port => 6380, :password => "secret"))
20
+
21
+ assert "bar" == redis.get("foo")
22
+ end
23
+ end
24
+
25
+ test "PING" do |r|
26
+ assert "PONG" == r.ping
27
+ end
28
+
29
+ test "SELECT" do |r|
30
+ r.set "foo", "bar"
31
+
32
+ r.select 14
33
+ assert nil == r.get("foo")
34
+
35
+ r.client.disconnect
36
+
37
+ assert nil == r.get("foo")
38
+ end
39
+
40
+ test "QUIT" do |r|
41
+ r.quit
42
+
43
+ assert !r.client.connected?
44
+ end
45
+
46
+ test "SHUTDOWN" do
47
+ redis_mock(:shutdown => lambda { "+SHUTDOWN" }) do
48
+ redis = Redis.new(OPTIONS.merge(:port => 6380))
49
+
50
+ assert "SHUTDOWN" == redis.shutdown
51
+ end
52
+ end
53
+
54
+ test "SLAVEOF" do
55
+ redis_mock(:slaveof => lambda { |host, port| "+SLAVEOF #{host} #{port}" }) do
56
+ redis = Redis.new(OPTIONS.merge(:port => 6380))
57
+
58
+ assert "SLAVEOF localhost 6381" == redis.slaveof("localhost", 6381)
59
+ end
60
+ end
61
+
62
+ test "BGREWRITEAOF" do
63
+ redis_mock(:bgrewriteaof => lambda { "+BGREWRITEAOF" }) do
64
+ redis = Redis.new(OPTIONS.merge(:port => 6380))
65
+
66
+ assert "BGREWRITEAOF" == redis.bgrewriteaof
67
+ end
68
+ end
69
+
70
+ test "CONFIG GET" do |r|
71
+ assert "300" == r.config(:get, "*")["timeout"]
72
+
73
+ assert r.config(:get, "timeout") == { "timeout" => "300" }
74
+ end
75
+
76
+ test "CONFIG SET" do |r|
77
+ begin
78
+ assert "OK" == r.config(:set, "timeout", 200)
79
+ assert "200" == r.config(:get, "*")["timeout"]
80
+
81
+ assert "OK" == r.config(:set, "timeout", 100)
82
+ assert "100" == r.config(:get, "*")["timeout"]
83
+ ensure
84
+ r.config :set, "timeout", 300
85
+ end
86
+ end
87
+
@@ -0,0 +1 @@
1
+ redis.pid
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ test "BLPOP" do |r|
12
+ r.lpush("foo", "s1")
13
+ r.lpush("foo", "s2")
14
+
15
+ wire = Wire.new do
16
+ redis = Redis::Distributed.new(NODES)
17
+ Wire.sleep 0.3
18
+ redis.lpush("foo", "s3")
19
+ end
20
+
21
+ assert ["foo", "s2"] == r.blpop("foo", 1)
22
+ assert ["foo", "s1"] == r.blpop("foo", 1)
23
+ assert ["foo", "s3"] == r.blpop("foo", 1)
24
+
25
+ wire.join
26
+ end
27
+
28
+ test "BRPOP" do |r|
29
+ r.rpush("foo", "s1")
30
+ r.rpush("foo", "s2")
31
+
32
+ wire = Wire.new do
33
+ redis = Redis::Distributed.new(NODES)
34
+ Wire.sleep 0.3
35
+ redis.rpush("foo", "s3")
36
+ end
37
+
38
+ assert ["foo", "s2"] == r.brpop("foo", 1)
39
+ assert ["foo", "s1"] == r.brpop("foo", 1)
40
+ assert ["foo", "s3"] == r.brpop("foo", 1)
41
+
42
+ wire.join
43
+ end
44
+
45
+ test "BRPOP should unset a configured socket timeout" do |r|
46
+ r = Redis::Distributed.new(NODES, :timeout => 1)
47
+
48
+ assert_nothing_raised do
49
+ r.brpop("foo", 2)
50
+ end # Errno::EAGAIN raised if socket times out before redis command times out
51
+
52
+ assert r.nodes.all? { |node| node.client.timeout == 1 }
53
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ load './test/lint/hashes.rb'
12
+
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ load './test/lint/lists.rb'
12
+
13
+ test "RPOPLPUSH" do |r|
14
+ assert_raise Redis::Distributed::CannotDistribute do
15
+ r.rpoplpush("foo", "bar")
16
+ end
17
+ end
18
+
19
+ test "BRPOPLPUSH" do |r|
20
+ assert_raise Redis::Distributed::CannotDistribute do
21
+ r.brpoplpush("foo", "bar", 1)
22
+ end
23
+ end
24
+
@@ -0,0 +1,85 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ load './test/lint/sets.rb'
12
+
13
+ test "SMOVE" do |r|
14
+ assert_raise Redis::Distributed::CannotDistribute do
15
+ r.sadd "foo", "s1"
16
+ r.sadd "bar", "s2"
17
+
18
+ r.smove("foo", "bar", "s1")
19
+ end
20
+ end
21
+
22
+ test "SINTER" do |r|
23
+ assert_raise Redis::Distributed::CannotDistribute do
24
+ r.sadd "foo", "s1"
25
+ r.sadd "foo", "s2"
26
+ r.sadd "bar", "s2"
27
+
28
+ r.sinter("foo", "bar")
29
+ end
30
+ end
31
+
32
+ test "SINTERSTORE" do |r|
33
+ assert_raise Redis::Distributed::CannotDistribute do
34
+ r.sadd "foo", "s1"
35
+ r.sadd "foo", "s2"
36
+ r.sadd "bar", "s2"
37
+
38
+ r.sinterstore("baz", "foo", "bar")
39
+ end
40
+ end
41
+
42
+ test "SUNION" do |r|
43
+ assert_raise Redis::Distributed::CannotDistribute do
44
+ r.sadd "foo", "s1"
45
+ r.sadd "foo", "s2"
46
+ r.sadd "bar", "s2"
47
+ r.sadd "bar", "s3"
48
+
49
+ r.sunion("foo", "bar")
50
+ end
51
+ end
52
+
53
+ test "SUNIONSTORE" do |r|
54
+ assert_raise Redis::Distributed::CannotDistribute do
55
+ r.sadd "foo", "s1"
56
+ r.sadd "foo", "s2"
57
+ r.sadd "bar", "s2"
58
+ r.sadd "bar", "s3"
59
+
60
+ r.sunionstore("baz", "foo", "bar")
61
+ end
62
+ end
63
+
64
+ test "SDIFF" do |r|
65
+ assert_raise Redis::Distributed::CannotDistribute do
66
+ r.sadd "foo", "s1"
67
+ r.sadd "foo", "s2"
68
+ r.sadd "bar", "s2"
69
+ r.sadd "bar", "s3"
70
+
71
+ r.sdiff("foo", "bar")
72
+ end
73
+ end
74
+
75
+ test "SDIFFSTORE" do |r|
76
+ assert_raise Redis::Distributed::CannotDistribute do
77
+ r.sadd "foo", "s1"
78
+ r.sadd "foo", "s2"
79
+ r.sadd "bar", "s2"
80
+ r.sadd "bar", "s3"
81
+
82
+ r.sdiffstore("baz", "foo", "bar")
83
+ end
84
+ end
85
+
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ load './test/lint/strings.rb'
12
+
13
+ test "MGET" do |r|
14
+ assert_raise Redis::Distributed::CannotDistribute do
15
+ r.mget("foo", "bar")
16
+ end
17
+ end
18
+
19
+ test "MGET mapped" do |r|
20
+ assert_raise Redis::Distributed::CannotDistribute do
21
+ r.mapped_mget("foo", "bar")
22
+ end
23
+ end
24
+
25
+ test "MSET" do |r|
26
+ assert_raise Redis::Distributed::CannotDistribute do
27
+ r.mset(:foo, "s1", :bar, "s2")
28
+ end
29
+ end
30
+
31
+ test "MSET mapped" do |r|
32
+ assert_raise Redis::Distributed::CannotDistribute do
33
+ r.mapped_mset(:foo => "s1", :bar => "s2")
34
+ end
35
+ end
36
+
37
+ test "MSETNX" do |r|
38
+ assert_raise Redis::Distributed::CannotDistribute do
39
+ r.set("foo", "s1")
40
+ r.msetnx(:foo, "s2", :bar, "s3")
41
+ end
42
+ end
43
+
44
+ test "MSETNX mapped" do |r|
45
+ assert_raise Redis::Distributed::CannotDistribute do
46
+ r.set("foo", "s1")
47
+ r.mapped_msetnx(:foo => "s2", :bar => "s3")
48
+ end
49
+ end
50
+
@@ -0,0 +1,73 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init(Redis::Distributed.new(NODES, :logger => ::Logger.new(log)))
9
+ end
10
+
11
+ load "./test/lint/value_types.rb"
12
+
13
+ test "DEL" do |r|
14
+ r.set "foo", "s1"
15
+ r.set "bar", "s2"
16
+ r.set "baz", "s3"
17
+
18
+ assert ["bar", "baz", "foo"] == r.keys("*").sort
19
+
20
+ assert 1 == r.del("foo")
21
+
22
+ assert ["bar", "baz"] == r.keys("*").sort
23
+
24
+ assert 2 == r.del("bar", "baz")
25
+
26
+ assert [] == r.keys("*").sort
27
+ end
28
+
29
+ test "RANDOMKEY" do |r|
30
+ assert_raise Redis::Distributed::CannotDistribute do
31
+ r.randomkey
32
+ end
33
+ end
34
+
35
+ test "RENAME" do |r|
36
+ assert_raise Redis::Distributed::CannotDistribute do
37
+ r.set("foo", "s1")
38
+ r.rename "foo", "bar"
39
+ end
40
+
41
+ assert "s1" == r.get("foo")
42
+ assert nil == r.get("bar")
43
+ end
44
+
45
+ test "RENAMENX" do |r|
46
+ assert_raise Redis::Distributed::CannotDistribute do
47
+ r.set("foo", "s1")
48
+ r.rename "foo", "bar"
49
+ end
50
+
51
+ assert "s1" == r.get("foo")
52
+ assert nil == r.get("bar")
53
+ end
54
+
55
+ test "DBSIZE" do |r|
56
+ assert [0] == r.dbsize
57
+
58
+ r.set("foo", "s1")
59
+
60
+ assert [1] == r.dbsize
61
+ end
62
+
63
+ test "FLUSHDB" do |r|
64
+ r.set("foo", "s1")
65
+ r.set("bar", "s2")
66
+
67
+ assert [2] == r.dbsize
68
+
69
+ r.flushdb
70
+
71
+ assert [0] == r.dbsize
72
+ end
73
+
@@ -0,0 +1,148 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ test "RENAME" do |r|
12
+ r.set("{qux}foo", "s1")
13
+ r.rename "{qux}foo", "{qux}bar"
14
+
15
+ assert "s1" == r.get("{qux}bar")
16
+ assert nil == r.get("{qux}foo")
17
+ end
18
+
19
+ test "RENAMENX" do |r|
20
+ r.set("{qux}foo", "s1")
21
+ r.set("{qux}bar", "s2")
22
+
23
+ assert false == r.renamenx("{qux}foo", "{qux}bar")
24
+
25
+ assert "s1" == r.get("{qux}foo")
26
+ assert "s2" == r.get("{qux}bar")
27
+ end
28
+
29
+ test "BRPOPLPUSH" do |r|
30
+ r.rpush "{qux}foo", "s1"
31
+ r.rpush "{qux}foo", "s2"
32
+
33
+ assert_equal "s2", r.brpoplpush("{qux}foo", "{qux}bar", 1)
34
+ assert_equal ["s2"], r.lrange("{qux}bar", 0, -1)
35
+ end
36
+
37
+ test "RPOPLPUSH" do |r|
38
+ r.rpush "{qux}foo", "s1"
39
+ r.rpush "{qux}foo", "s2"
40
+
41
+ assert "s2" == r.rpoplpush("{qux}foo", "{qux}bar")
42
+ assert ["s2"] == r.lrange("{qux}bar", 0, -1)
43
+ assert "s1" == r.rpoplpush("{qux}foo", "{qux}bar")
44
+ assert ["s1", "s2"] == r.lrange("{qux}bar", 0, -1)
45
+ end
46
+
47
+ test "SMOVE" do |r|
48
+ r.sadd "{qux}foo", "s1"
49
+ r.sadd "{qux}bar", "s2"
50
+
51
+ assert r.smove("{qux}foo", "{qux}bar", "s1")
52
+ assert r.sismember("{qux}bar", "s1")
53
+ end
54
+
55
+ test "SINTER" do |r|
56
+ r.sadd "{qux}foo", "s1"
57
+ r.sadd "{qux}foo", "s2"
58
+ r.sadd "{qux}bar", "s2"
59
+
60
+ assert ["s2"] == r.sinter("{qux}foo", "{qux}bar")
61
+ end
62
+
63
+ test "SINTERSTORE" do |r|
64
+ r.sadd "{qux}foo", "s1"
65
+ r.sadd "{qux}foo", "s2"
66
+ r.sadd "{qux}bar", "s2"
67
+
68
+ r.sinterstore("{qux}baz", "{qux}foo", "{qux}bar")
69
+
70
+ assert ["s2"] == r.smembers("{qux}baz")
71
+ end
72
+
73
+ test "SUNION" do |r|
74
+ r.sadd "{qux}foo", "s1"
75
+ r.sadd "{qux}foo", "s2"
76
+ r.sadd "{qux}bar", "s2"
77
+ r.sadd "{qux}bar", "s3"
78
+
79
+ assert ["s1", "s2", "s3"] == r.sunion("{qux}foo", "{qux}bar").sort
80
+ end
81
+
82
+ test "SUNIONSTORE" do |r|
83
+ r.sadd "{qux}foo", "s1"
84
+ r.sadd "{qux}foo", "s2"
85
+ r.sadd "{qux}bar", "s2"
86
+ r.sadd "{qux}bar", "s3"
87
+
88
+ r.sunionstore("{qux}baz", "{qux}foo", "{qux}bar")
89
+
90
+ assert ["s1", "s2", "s3"] == r.smembers("{qux}baz").sort
91
+ end
92
+
93
+ test "SDIFF" do |r|
94
+ r.sadd "{qux}foo", "s1"
95
+ r.sadd "{qux}foo", "s2"
96
+ r.sadd "{qux}bar", "s2"
97
+ r.sadd "{qux}bar", "s3"
98
+
99
+ assert ["s1"] == r.sdiff("{qux}foo", "{qux}bar")
100
+ assert ["s3"] == r.sdiff("{qux}bar", "{qux}foo")
101
+ end
102
+
103
+ test "SDIFFSTORE" do |r|
104
+ r.sadd "{qux}foo", "s1"
105
+ r.sadd "{qux}foo", "s2"
106
+ r.sadd "{qux}bar", "s2"
107
+ r.sadd "{qux}bar", "s3"
108
+
109
+ r.sdiffstore("{qux}baz", "{qux}foo", "{qux}bar")
110
+
111
+ assert ["s1"] == r.smembers("{qux}baz")
112
+ end
113
+
114
+ test "SORT" do |r|
115
+ r.set("{qux}foo:1", "s1")
116
+ r.set("{qux}foo:2", "s2")
117
+
118
+ r.rpush("{qux}bar", "1")
119
+ r.rpush("{qux}bar", "2")
120
+
121
+ assert ["s1"] == r.sort("{qux}bar", :get => "{qux}foo:*", :limit => [0, 1])
122
+ assert ["s2"] == r.sort("{qux}bar", :get => "{qux}foo:*", :limit => [0, 1], :order => "desc alpha")
123
+ end
124
+
125
+ test "SORT with an array of GETs" do |r|
126
+ r.set("{qux}foo:1:a", "s1a")
127
+ r.set("{qux}foo:1:b", "s1b")
128
+
129
+ r.set("{qux}foo:2:a", "s2a")
130
+ r.set("{qux}foo:2:b", "s2b")
131
+
132
+ r.rpush("{qux}bar", "1")
133
+ r.rpush("{qux}bar", "2")
134
+
135
+ assert ["s1a", "s1b"] == r.sort("{qux}bar", :get => ["{qux}foo:*:a", "{qux}foo:*:b"], :limit => [0, 1])
136
+ assert ["s2a", "s2b"] == r.sort("{qux}bar", :get => ["{qux}foo:*:a", "{qux}foo:*:b"], :limit => [0, 1], :order => "desc alpha")
137
+ end
138
+
139
+ test "SORT with STORE" do |r|
140
+ r.set("{qux}foo:1", "s1")
141
+ r.set("{qux}foo:2", "s2")
142
+
143
+ r.rpush("{qux}bar", "1")
144
+ r.rpush("{qux}bar", "2")
145
+
146
+ r.sort("{qux}bar", :get => "{qux}foo:*", :store => "{qux}baz")
147
+ assert ["s1", "s2"] == r.lrange("{qux}baz", 0, -1)
148
+ end