gorsuch-redis 3.0.0.rc1

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 (87) hide show
  1. data/.gitignore +10 -0
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +113 -0
  4. data/LICENSE +20 -0
  5. data/README.md +214 -0
  6. data/Rakefile +260 -0
  7. data/TODO.md +4 -0
  8. data/benchmarking/logging.rb +62 -0
  9. data/benchmarking/pipeline.rb +51 -0
  10. data/benchmarking/speed.rb +21 -0
  11. data/benchmarking/suite.rb +24 -0
  12. data/benchmarking/thread_safety.rb +38 -0
  13. data/benchmarking/worker.rb +71 -0
  14. data/examples/basic.rb +15 -0
  15. data/examples/dist_redis.rb +43 -0
  16. data/examples/incr-decr.rb +17 -0
  17. data/examples/list.rb +26 -0
  18. data/examples/pubsub.rb +31 -0
  19. data/examples/sets.rb +36 -0
  20. data/examples/unicorn/config.ru +3 -0
  21. data/examples/unicorn/unicorn.rb +20 -0
  22. data/lib/redis/client.rb +303 -0
  23. data/lib/redis/connection/command_helper.rb +44 -0
  24. data/lib/redis/connection/hiredis.rb +52 -0
  25. data/lib/redis/connection/registry.rb +12 -0
  26. data/lib/redis/connection/ruby.rb +136 -0
  27. data/lib/redis/connection/synchrony.rb +131 -0
  28. data/lib/redis/connection.rb +9 -0
  29. data/lib/redis/distributed.rb +696 -0
  30. data/lib/redis/errors.rb +38 -0
  31. data/lib/redis/hash_ring.rb +131 -0
  32. data/lib/redis/pipeline.rb +106 -0
  33. data/lib/redis/subscribe.rb +79 -0
  34. data/lib/redis/version.rb +3 -0
  35. data/lib/redis.rb +1724 -0
  36. data/redis.gemspec +43 -0
  37. data/test/command_map_test.rb +29 -0
  38. data/test/commands_on_hashes_test.rb +20 -0
  39. data/test/commands_on_lists_test.rb +60 -0
  40. data/test/commands_on_sets_test.rb +76 -0
  41. data/test/commands_on_sorted_sets_test.rb +108 -0
  42. data/test/commands_on_strings_test.rb +80 -0
  43. data/test/commands_on_value_types_test.rb +87 -0
  44. data/test/connection_handling_test.rb +204 -0
  45. data/test/db/.gitignore +1 -0
  46. data/test/distributed_blocking_commands_test.rb +53 -0
  47. data/test/distributed_commands_on_hashes_test.rb +11 -0
  48. data/test/distributed_commands_on_lists_test.rb +23 -0
  49. data/test/distributed_commands_on_sets_test.rb +84 -0
  50. data/test/distributed_commands_on_sorted_sets_test.rb +19 -0
  51. data/test/distributed_commands_on_strings_test.rb +49 -0
  52. data/test/distributed_commands_on_value_types_test.rb +72 -0
  53. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  54. data/test/distributed_connection_handling_test.rb +24 -0
  55. data/test/distributed_internals_test.rb +27 -0
  56. data/test/distributed_key_tags_test.rb +52 -0
  57. data/test/distributed_persistence_control_commands_test.rb +23 -0
  58. data/test/distributed_publish_subscribe_test.rb +100 -0
  59. data/test/distributed_remote_server_control_commands_test.rb +42 -0
  60. data/test/distributed_sorting_test.rb +21 -0
  61. data/test/distributed_test.rb +59 -0
  62. data/test/distributed_transactions_test.rb +33 -0
  63. data/test/encoding_test.rb +15 -0
  64. data/test/error_replies_test.rb +53 -0
  65. data/test/helper.rb +155 -0
  66. data/test/helper_test.rb +8 -0
  67. data/test/internals_test.rb +152 -0
  68. data/test/lint/hashes.rb +140 -0
  69. data/test/lint/internals.rb +36 -0
  70. data/test/lint/lists.rb +107 -0
  71. data/test/lint/sets.rb +90 -0
  72. data/test/lint/sorted_sets.rb +196 -0
  73. data/test/lint/strings.rb +133 -0
  74. data/test/lint/value_types.rb +81 -0
  75. data/test/persistence_control_commands_test.rb +21 -0
  76. data/test/pipelining_commands_test.rb +186 -0
  77. data/test/publish_subscribe_test.rb +158 -0
  78. data/test/redis_mock.rb +89 -0
  79. data/test/remote_server_control_commands_test.rb +88 -0
  80. data/test/sorting_test.rb +43 -0
  81. data/test/synchrony_driver.rb +57 -0
  82. data/test/test.conf +9 -0
  83. data/test/thread_safety_test.rb +30 -0
  84. data/test/transactions_test.rb +173 -0
  85. data/test/unknown_commands_test.rb +13 -0
  86. data/test/url_param_test.rb +59 -0
  87. metadata +236 -0
@@ -0,0 +1,133 @@
1
+ require File.expand_path("../redis_mock", File.dirname(__FILE__))
2
+
3
+ include RedisMock::Helper
4
+
5
+ test "SET and GET" do |r|
6
+ r.set("foo", "s1")
7
+
8
+ assert "s1" == r.get("foo")
9
+ end
10
+
11
+ test "SET and GET with brackets" do |r|
12
+ r["foo"] = "s1"
13
+
14
+ assert "s1" == r["foo"]
15
+ end
16
+
17
+ test "SET and GET with brackets and symbol" do |r|
18
+ r[:foo] = "s1"
19
+
20
+ assert "s1" == r[:foo]
21
+ end
22
+
23
+ test "SET and GET with newline characters" do |r|
24
+ r.set("foo", "1\n")
25
+
26
+ assert "1\n" == r.get("foo")
27
+ end
28
+
29
+ test "SET and GET with ASCII characters" do |r|
30
+ with_external_encoding("ASCII-8BIT") do
31
+ (0..255).each do |i|
32
+ str = "#{i.chr}---#{i.chr}"
33
+ r.set("foo", str)
34
+
35
+ assert str == r.get("foo")
36
+ end
37
+ end
38
+ end if defined?(Encoding)
39
+
40
+ test "SETEX" do
41
+ redis_mock(:setex => lambda { |*args| "+#{args.join(" ")}" }) do
42
+ r = Redis.new(OPTIONS.merge(:port => 6380))
43
+
44
+ assert_equal "foo 1 s1", r.setex("foo", 1, "s1")
45
+ end
46
+ end
47
+
48
+ test "GETSET" do |r|
49
+ r.set("foo", "bar")
50
+
51
+ assert "bar" == r.getset("foo", "baz")
52
+ assert "baz" == r.get("foo")
53
+ end
54
+
55
+ test "SETNX" do |r|
56
+ r.set("foo", "s1")
57
+
58
+ assert "s1" == r.get("foo")
59
+
60
+ r.setnx("foo", "s2")
61
+
62
+ assert "s1" == r.get("foo")
63
+ end
64
+
65
+ test "INCR" do |r|
66
+ assert 1 == r.incr("foo")
67
+ assert 2 == r.incr("foo")
68
+ assert 3 == r.incr("foo")
69
+ end
70
+
71
+ test "INCRBY" do |r|
72
+ assert 1 == r.incrby("foo", 1)
73
+ assert 3 == r.incrby("foo", 2)
74
+ assert 6 == r.incrby("foo", 3)
75
+ end
76
+
77
+ test "DECR" do |r|
78
+ r.set("foo", 3)
79
+
80
+ assert 2 == r.decr("foo")
81
+ assert 1 == r.decr("foo")
82
+ assert 0 == r.decr("foo")
83
+ end
84
+
85
+ test "DECRBY" do |r|
86
+ r.set("foo", 6)
87
+
88
+ assert 3 == r.decrby("foo", 3)
89
+ assert 1 == r.decrby("foo", 2)
90
+ assert 0 == r.decrby("foo", 1)
91
+ end
92
+
93
+ test "APPEND" do |r|
94
+ r.set "foo", "s"
95
+ r.append "foo", "1"
96
+
97
+ assert "s1" == r.get("foo")
98
+ end
99
+
100
+ test "GETBIT" do |r|
101
+ r.set("foo", "a")
102
+
103
+ assert_equal 1, r.getbit("foo", 1)
104
+ assert_equal 1, r.getbit("foo", 2)
105
+ assert_equal 0, r.getbit("foo", 3)
106
+ assert_equal 0, r.getbit("foo", 4)
107
+ assert_equal 0, r.getbit("foo", 5)
108
+ assert_equal 0, r.getbit("foo", 6)
109
+ assert_equal 1, r.getbit("foo", 7)
110
+ end
111
+
112
+ test "SETBIT" do |r|
113
+ r.set("foo", "a")
114
+
115
+ r.setbit("foo", 6, 1)
116
+
117
+ assert_equal "c", r.get("foo")
118
+ end
119
+
120
+ test "GETRANGE" do |r|
121
+ r.set("foo", "abcde")
122
+
123
+ assert_equal "bcd", r.getrange("foo", 1, 3)
124
+ assert_equal "abcde", r.getrange("foo", 0, -1)
125
+ end
126
+
127
+ test "SETRANGE" do |r|
128
+ r.set("foo", "abcde")
129
+
130
+ r.setrange("foo", 1, "bar")
131
+
132
+ assert_equal "abare", r.get("foo")
133
+ end
@@ -0,0 +1,81 @@
1
+ require File.expand_path("../redis_mock", File.dirname(__FILE__))
2
+
3
+ include RedisMock::Helper
4
+
5
+ test "EXISTS" do |r|
6
+ assert false == r.exists("foo")
7
+
8
+ r.set("foo", "s1")
9
+
10
+ assert true == r.exists("foo")
11
+ end
12
+
13
+ test "TYPE" do |r|
14
+ assert "none" == r.type("foo")
15
+
16
+ r.set("foo", "s1")
17
+
18
+ assert "string" == r.type("foo")
19
+ end
20
+
21
+ test "KEYS" do |r|
22
+ r.set("f", "s1")
23
+ r.set("fo", "s2")
24
+ r.set("foo", "s3")
25
+
26
+ assert ["f","fo", "foo"] == r.keys("f*").sort
27
+ end
28
+
29
+ test "EXPIRE" do |r|
30
+ redis_mock(:expire => lambda { |*args| args == ["foo", "1"] ? ":1" : ":0" }) do
31
+ r = Redis.new(OPTIONS.merge(:port => 6380))
32
+
33
+ assert r.expire("foo", 1)
34
+ end
35
+ end
36
+
37
+ test "EXPIREAT" do |r|
38
+ redis_mock(:expireat => lambda { |*args| args == ["foo", "1328236326"] ? ":1" : ":0" }) do
39
+ r = Redis.new(OPTIONS.merge(:port => 6380))
40
+
41
+ assert r.expireat("foo", 1328236326)
42
+ end
43
+ end
44
+
45
+ test "PERSIST" do |r|
46
+ r.set("foo", "s1")
47
+ r.expire("foo", 1)
48
+ r.persist("foo")
49
+
50
+ assert(-1 == r.ttl("foo"))
51
+ end
52
+
53
+ test "TTL" do |r|
54
+ r.set("foo", "s1")
55
+ r.expire("foo", 1)
56
+
57
+ assert 1 == r.ttl("foo")
58
+ end
59
+
60
+ test "MOVE" do |r|
61
+ r.select 14
62
+ r.flushdb
63
+
64
+ r.set "bar", "s3"
65
+
66
+ r.select 15
67
+
68
+ r.set "foo", "s1"
69
+ r.set "bar", "s2"
70
+
71
+ assert r.move("foo", 14)
72
+ assert nil == r.get("foo")
73
+
74
+ assert !r.move("bar", 14)
75
+ assert "s2" == r.get("bar")
76
+
77
+ r.select 14
78
+
79
+ assert "s1" == r.get("foo")
80
+ assert "s3" == r.get("bar")
81
+ end
@@ -0,0 +1,21 @@
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 "SAVE and BGSAVE" do |r|
10
+ assert_nothing_raised do
11
+ r.save
12
+ end
13
+
14
+ assert_nothing_raised do
15
+ r.bgsave
16
+ end
17
+ end
18
+
19
+ test "LASTSAVE" do |r|
20
+ assert Time.at(r.lastsave) <= Time.now
21
+ end
@@ -0,0 +1,186 @@
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 "BULK commands" do |r|
10
+ r.pipelined do
11
+ r.lpush "foo", "s1"
12
+ r.lpush "foo", "s2"
13
+ end
14
+
15
+ assert 2 == r.llen("foo")
16
+ assert "s2" == r.lpop("foo")
17
+ assert "s1" == r.lpop("foo")
18
+ end
19
+
20
+ test "MULTI_BULK commands" do |r|
21
+ r.pipelined do
22
+ r.mset("foo", "s1", "bar", "s2")
23
+ r.mset("baz", "s3", "qux", "s4")
24
+ end
25
+
26
+ assert "s1" == r.get("foo")
27
+ assert "s2" == r.get("bar")
28
+ assert "s3" == r.get("baz")
29
+ assert "s4" == r.get("qux")
30
+ end
31
+
32
+ test "BULK and MULTI_BULK commands mixed" do |r|
33
+ r.pipelined do
34
+ r.lpush "foo", "s1"
35
+ r.lpush "foo", "s2"
36
+ r.mset("baz", "s3", "qux", "s4")
37
+ end
38
+
39
+ assert 2 == r.llen("foo")
40
+ assert "s2" == r.lpop("foo")
41
+ assert "s1" == r.lpop("foo")
42
+ assert "s3" == r.get("baz")
43
+ assert "s4" == r.get("qux")
44
+ end
45
+
46
+ test "MULTI_BULK and BULK commands mixed" do |r|
47
+ r.pipelined do
48
+ r.mset("baz", "s3", "qux", "s4")
49
+ r.lpush "foo", "s1"
50
+ r.lpush "foo", "s2"
51
+ end
52
+
53
+ assert 2 == r.llen("foo")
54
+ assert "s2" == r.lpop("foo")
55
+ assert "s1" == r.lpop("foo")
56
+ assert "s3" == r.get("baz")
57
+ assert "s4" == r.get("qux")
58
+ end
59
+
60
+ test "Pipelined with an empty block" do |r|
61
+ assert_nothing_raised do
62
+ r.pipelined do
63
+ end
64
+ end
65
+
66
+ assert 0 == r.dbsize
67
+ end
68
+
69
+ test "Returning the result of a pipeline" do |r|
70
+ result = r.pipelined do
71
+ r.set "foo", "bar"
72
+ r.get "foo"
73
+ r.get "bar"
74
+ end
75
+
76
+ assert ["OK", "bar", nil] == result
77
+ end
78
+
79
+ test "Assignment of results inside the block" do |r|
80
+ r.pipelined do
81
+ @first = r.sadd("foo", 1)
82
+ @second = r.sadd("foo", 1)
83
+ end
84
+
85
+ assert_equal true, @first.value
86
+ assert_equal false, @second.value
87
+ end
88
+
89
+ # Although we could support accessing the values in these futures,
90
+ # it doesn't make a lot of sense.
91
+ test "Assignment of results inside the block with errors" do |r|
92
+ assert_raise do
93
+ r.pipelined do
94
+ r.doesnt_exist
95
+ @first = r.sadd("foo", 1)
96
+ r.doesnt_exist
97
+ @second = r.sadd("foo", 1)
98
+ r.doesnt_exist
99
+ end
100
+ end
101
+
102
+ assert_raise(Redis::FutureNotReady) { @first.value }
103
+ assert_raise(Redis::FutureNotReady) { @second.value }
104
+ end
105
+
106
+ test "Assignment of results inside a nested block" do |r|
107
+ r.pipelined do
108
+ @first = r.sadd("foo", 1)
109
+
110
+ r.pipelined do
111
+ @second = r.sadd("foo", 1)
112
+ end
113
+ end
114
+
115
+ assert_equal true, @first.value
116
+ assert_equal false, @second.value
117
+ end
118
+
119
+ test "Futures raise when confused with something else" do |r|
120
+ r.pipelined do
121
+ @result = r.sadd("foo", 1)
122
+ end
123
+
124
+ assert_raise(NoMethodError) { @result.to_s }
125
+ end
126
+
127
+ test "Futures raise when trying to access their values too early" do |r|
128
+ r.pipelined do
129
+ assert_raise(Redis::FutureNotReady) do
130
+ r.sadd("foo", 1).value
131
+ end
132
+ end
133
+ end
134
+
135
+ test "Returning the result of an empty pipeline" do |r|
136
+ result = r.pipelined do
137
+ end
138
+
139
+ assert [] == result
140
+ end
141
+
142
+ test "Nesting pipeline blocks" do |r|
143
+ r.pipelined do
144
+ r.set("foo", "s1")
145
+ r.pipelined do
146
+ r.set("bar", "s2")
147
+ end
148
+ end
149
+
150
+ assert "s1" == r.get("foo")
151
+ assert "s2" == r.get("bar")
152
+ end
153
+
154
+ test "INFO in a pipeline returns hash" do |r|
155
+ result = r.pipelined do
156
+ r.info
157
+ end
158
+
159
+ assert result.first.kind_of?(Hash)
160
+ end
161
+
162
+ test "CONFIG GET in a pipeline returns hash" do |r|
163
+ result = r.pipelined do
164
+ r.config(:get, "*")
165
+ end
166
+
167
+ assert result.first.kind_of?(Hash)
168
+ end
169
+
170
+ test "HGETALL in a pipeline returns hash" do |r|
171
+ r.hmset("hash", "field", "value")
172
+ result = r.pipelined do
173
+ r.hgetall("hash")
174
+ end
175
+
176
+ assert result.first == { "field" => "value" }
177
+ end
178
+
179
+ test "KEYS in a pipeline" do |r|
180
+ r.set("key", "value")
181
+ result = r.pipelined do
182
+ r.keys("*")
183
+ end
184
+
185
+ assert ["key"] == result.first
186
+ end
@@ -0,0 +1,158 @@
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 "SUBSCRIBE and UNSUBSCRIBE" do |r|
10
+ listening = false
11
+
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
18
+
19
+ on.message do |channel, message|
20
+ if message == "s1"
21
+ r.unsubscribe
22
+ @message = message
23
+ end
24
+ end
25
+
26
+ on.unsubscribe do |channel, total|
27
+ @unsubscribed = true
28
+ @t2 = total
29
+ end
30
+
31
+ listening = true
32
+ end
33
+ end
34
+
35
+ Wire.pass while !listening
36
+
37
+ Redis.new(OPTIONS).publish("foo", "s1")
38
+
39
+ wire.join
40
+
41
+ assert @subscribed
42
+ assert 1 == @t1
43
+ assert @unsubscribed
44
+ assert 0 == @t2
45
+ assert "s1" == @message
46
+ end
47
+
48
+ test "PSUBSCRIBE and PUNSUBSCRIBE" do |r|
49
+ listening = false
50
+
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
57
+
58
+ on.pmessage do |pattern, channel, message|
59
+ if message == "s1"
60
+ r.punsubscribe
61
+ @message = message
62
+ end
63
+ end
64
+
65
+ on.punsubscribe do |pattern, total|
66
+ @unsubscribed = true
67
+ @t2 = total
68
+ end
69
+
70
+ listening = true
71
+ end
72
+ end
73
+
74
+ Wire.pass while !listening
75
+
76
+ Redis.new(OPTIONS).publish("foo", "s1")
77
+
78
+ wire.join
79
+
80
+ assert @subscribed
81
+ assert 1 == @t1
82
+ assert @unsubscribed
83
+ assert 0 == @t2
84
+ assert "s1" == @message
85
+ end
86
+
87
+ test "SUBSCRIBE within SUBSCRIBE" do |r|
88
+ listening = false
89
+
90
+ @channels = []
91
+
92
+ wire = Wire.new do
93
+ r.subscribe("foo") do |on|
94
+ on.subscribe do |channel, total|
95
+ @channels << channel
96
+
97
+ r.subscribe("bar") if channel == "foo"
98
+ r.unsubscribe if channel == "bar"
99
+ end
100
+
101
+ listening = true
102
+ end
103
+ end
104
+
105
+ Wire.pass while !listening
106
+
107
+ Redis.new(OPTIONS).publish("foo", "s1")
108
+
109
+ wire.join
110
+
111
+ assert ["foo", "bar"] == @channels
112
+ end
113
+
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")
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ test "SUBSCRIBE without a block" do |r|
125
+ assert_raise LocalJumpError do
126
+ r.subscribe("foo")
127
+ end
128
+ end
129
+
130
+ test "UNSUBSCRIBE without a SUBSCRIBE" do |r|
131
+ assert_raise RuntimeError do
132
+ r.unsubscribe
133
+ end
134
+
135
+ assert_raise RuntimeError do
136
+ r.punsubscribe
137
+ end
138
+ end
139
+
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("; ")
145
+
146
+ IO.popen(cmd, "r+") do |pipe|
147
+ received = false
148
+
149
+ r.subscribe "foo" do |on|
150
+ on.message do |channel, message|
151
+ received = true
152
+ r.unsubscribe
153
+ end
154
+ end
155
+
156
+ assert received
157
+ end
158
+ end