redis 2.0.0 → 2.2.1
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.
- data/.gitignore +8 -0
- data/CHANGELOG.md +49 -0
- data/README.md +208 -0
- data/Rakefile +183 -73
- data/TODO.md +4 -0
- data/benchmarking/logging.rb +62 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/thread_safety.rb +38 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/dist_redis.rb +43 -0
- data/examples/incr-decr.rb +17 -0
- data/examples/list.rb +26 -0
- data/examples/pubsub.rb +31 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis/client.rb +149 -165
- data/lib/redis/connection/command_helper.rb +45 -0
- data/lib/redis/connection/hiredis.rb +49 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +135 -0
- data/lib/redis/connection/synchrony.rb +129 -0
- data/lib/redis/connection.rb +9 -0
- data/lib/redis/distributed.rb +182 -7
- data/lib/redis/pipeline.rb +22 -1
- data/lib/redis/subscribe.rb +19 -4
- data/lib/redis/version.rb +3 -0
- data/lib/redis.rb +715 -155
- data/redis.gemspec +24 -0
- data/test/commands_on_hashes_test.rb +32 -0
- data/test/commands_on_lists_test.rb +60 -0
- data/test/commands_on_sets_test.rb +78 -0
- data/test/commands_on_sorted_sets_test.rb +109 -0
- data/test/commands_on_strings_test.rb +80 -0
- data/test/commands_on_value_types_test.rb +88 -0
- data/test/connection_handling_test.rb +88 -0
- data/test/db/.gitignore +1 -0
- data/test/distributed_blocking_commands_test.rb +53 -0
- data/test/distributed_commands_on_hashes_test.rb +12 -0
- data/test/distributed_commands_on_lists_test.rb +24 -0
- data/test/distributed_commands_on_sets_test.rb +85 -0
- data/test/distributed_commands_on_strings_test.rb +50 -0
- data/test/distributed_commands_on_value_types_test.rb +73 -0
- data/test/distributed_commands_requiring_clustering_test.rb +148 -0
- data/test/distributed_connection_handling_test.rb +25 -0
- data/test/distributed_internals_test.rb +27 -0
- data/test/distributed_key_tags_test.rb +53 -0
- data/test/distributed_persistence_control_commands_test.rb +24 -0
- data/test/distributed_publish_subscribe_test.rb +101 -0
- data/test/distributed_remote_server_control_commands_test.rb +43 -0
- data/test/distributed_sorting_test.rb +21 -0
- data/test/distributed_test.rb +59 -0
- data/test/distributed_transactions_test.rb +34 -0
- data/test/encoding_test.rb +16 -0
- data/test/error_replies_test.rb +53 -0
- data/test/helper.rb +145 -0
- data/test/internals_test.rb +160 -0
- data/test/lint/hashes.rb +114 -0
- data/test/lint/internals.rb +37 -0
- data/test/lint/lists.rb +93 -0
- data/test/lint/sets.rb +66 -0
- data/test/lint/sorted_sets.rb +167 -0
- data/test/lint/strings.rb +137 -0
- data/test/lint/value_types.rb +84 -0
- data/test/persistence_control_commands_test.rb +22 -0
- data/test/pipelining_commands_test.rb +123 -0
- data/test/publish_subscribe_test.rb +158 -0
- data/test/redis_mock.rb +80 -0
- data/test/remote_server_control_commands_test.rb +82 -0
- data/test/sorting_test.rb +44 -0
- data/test/synchrony_driver.rb +57 -0
- data/test/test.conf +8 -0
- data/test/thread_safety_test.rb +30 -0
- data/test/transactions_test.rb +100 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +60 -0
- metadata +130 -22
- data/README.markdown +0 -120
|
@@ -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 RuntimeError 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
|
data/test/redis_mock.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
3
|
+
module RedisMock
|
|
4
|
+
class Server
|
|
5
|
+
VERBOSE = false
|
|
6
|
+
|
|
7
|
+
def initialize(port = 6380, &block)
|
|
8
|
+
@server = TCPServer.new("127.0.0.1", port)
|
|
9
|
+
@server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
|
10
|
+
@thread = Thread.new { run(&block) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# This raises an exception in the thread calling @server.accept which
|
|
14
|
+
# in turn will cause the thread to terminate.
|
|
15
|
+
def shutdown
|
|
16
|
+
@server.close if @server
|
|
17
|
+
rescue => ex
|
|
18
|
+
$stderr.puts "Error closing mock server: #{ex.message}" if VERBOSE
|
|
19
|
+
$stderr.puts ex.backtrace if VERBOSE
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
loop do
|
|
24
|
+
session = @server.accept
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
while line = session.gets
|
|
28
|
+
parts = Array.new(line[1..-3].to_i) do
|
|
29
|
+
bytes = session.gets[1..-3].to_i
|
|
30
|
+
argument = session.read(bytes)
|
|
31
|
+
session.read(2) # Discard \r\n
|
|
32
|
+
argument
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
response = yield(*parts)
|
|
36
|
+
|
|
37
|
+
if response.nil?
|
|
38
|
+
session.shutdown(Socket::SHUT_RDWR)
|
|
39
|
+
break
|
|
40
|
+
else
|
|
41
|
+
session.write(response)
|
|
42
|
+
session.write("\r\n")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
rescue Errno::ECONNRESET
|
|
46
|
+
# Ignore client closing the connection
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
rescue => ex
|
|
50
|
+
$stderr.puts "Error running mock server: #{ex.message}" if VERBOSE
|
|
51
|
+
$stderr.puts ex.backtrace if VERBOSE
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module Helper
|
|
56
|
+
# Starts a mock Redis server in a thread on port 6380.
|
|
57
|
+
#
|
|
58
|
+
# The server will reply with a `+OK` to all commands, but you can
|
|
59
|
+
# customize it by providing a hash. For example:
|
|
60
|
+
#
|
|
61
|
+
# redis_mock(:ping => lambda { "+PONG" }) do
|
|
62
|
+
# assert_equal "PONG", Redis.new(:port => 6380).ping
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
def redis_mock(replies = {})
|
|
66
|
+
begin
|
|
67
|
+
server = Server.new do |command, *args|
|
|
68
|
+
(replies[command.to_sym] || lambda { |*_| "+OK" }).call(*args)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
sleep 0.1 # Give time for the socket to start listening.
|
|
72
|
+
|
|
73
|
+
yield
|
|
74
|
+
|
|
75
|
+
ensure
|
|
76
|
+
server.shutdown
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
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 "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)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "INFO COMMANDSTATS" do |r|
|
|
19
|
+
# Only available on Redis >= 2.3.0
|
|
20
|
+
next if r.info["redis_version"] < "2.3.0"
|
|
21
|
+
|
|
22
|
+
r.config(:resetstat)
|
|
23
|
+
r.ping
|
|
24
|
+
|
|
25
|
+
result = r.info(:commandstats)
|
|
26
|
+
assert "1" == result["ping"]["calls"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
test "MONITOR" do |r|
|
|
30
|
+
log = []
|
|
31
|
+
|
|
32
|
+
wire = Wire.new do
|
|
33
|
+
Redis.new(OPTIONS).monitor do |line|
|
|
34
|
+
log << line
|
|
35
|
+
break if log.size == 3
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Wire.pass while log.empty? # Faster than sleep
|
|
40
|
+
|
|
41
|
+
r.set "foo", "s1"
|
|
42
|
+
|
|
43
|
+
wire.join
|
|
44
|
+
|
|
45
|
+
assert log[-1][%q{(db 15) "set" "foo" "s1"}]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test "MONITOR returns value for break" do |r|
|
|
49
|
+
result = r.monitor do |line|
|
|
50
|
+
break line
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
assert result == "OK"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "ECHO" do |r|
|
|
57
|
+
assert "foo bar baz\n" == r.echo("foo bar baz\n")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test "DEBUG" do |r|
|
|
61
|
+
r.set "foo", "s1"
|
|
62
|
+
|
|
63
|
+
assert r.debug(:object, "foo").kind_of?(String)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test "OBJECT" do |r|
|
|
67
|
+
r.lpush "list", "value"
|
|
68
|
+
|
|
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
|
|
73
|
+
|
|
74
|
+
test "SYNC" do |r|
|
|
75
|
+
replies = {:sync => lambda { "+OK" }}
|
|
76
|
+
|
|
77
|
+
redis_mock(replies) do
|
|
78
|
+
redis = Redis.new(OPTIONS.merge(:port => 6380))
|
|
79
|
+
|
|
80
|
+
assert "OK" == redis.sync
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
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 "SORT" do |r|
|
|
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 ["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
|
|
19
|
+
|
|
20
|
+
test "SORT with an array of GETs" do |r|
|
|
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 ["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
|
|
33
|
+
|
|
34
|
+
test "SORT with STORE" do |r|
|
|
35
|
+
r.set("foo:1", "s1")
|
|
36
|
+
r.set("foo:2", "s2")
|
|
37
|
+
|
|
38
|
+
r.rpush("bar", "1")
|
|
39
|
+
r.rpush("bar", "2")
|
|
40
|
+
|
|
41
|
+
r.sort("bar", :get => "foo:*", :store => "baz")
|
|
42
|
+
assert ["s1", "s2"] == r.lrange("baz", 0, -1)
|
|
43
|
+
end
|
|
44
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'em-synchrony'
|
|
4
|
+
|
|
5
|
+
require 'redis'
|
|
6
|
+
require 'redis/connection/synchrony'
|
|
7
|
+
|
|
8
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# if running under Eventmachine + Synchrony (Ruby 1.9+), then
|
|
12
|
+
# we can simulate the blocking API while performing the network
|
|
13
|
+
# IO via the EM reactor.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
EM.synchrony do
|
|
17
|
+
r = Redis.new
|
|
18
|
+
r.flushdb
|
|
19
|
+
|
|
20
|
+
r.rpush "foo", "s1"
|
|
21
|
+
r.rpush "foo", "s2"
|
|
22
|
+
|
|
23
|
+
assert 2 == r.llen("foo")
|
|
24
|
+
assert "s2" == r.rpop("foo")
|
|
25
|
+
|
|
26
|
+
r.set("foo", "bar")
|
|
27
|
+
|
|
28
|
+
assert "bar" == r.getset("foo", "baz")
|
|
29
|
+
assert "baz" == r.get("foo")
|
|
30
|
+
|
|
31
|
+
r.set("foo", "a")
|
|
32
|
+
|
|
33
|
+
assert_equal 1, r.getbit("foo", 1)
|
|
34
|
+
assert_equal 1, r.getbit("foo", 2)
|
|
35
|
+
assert_equal 0, r.getbit("foo", 3)
|
|
36
|
+
assert_equal 0, r.getbit("foo", 4)
|
|
37
|
+
assert_equal 0, r.getbit("foo", 5)
|
|
38
|
+
assert_equal 0, r.getbit("foo", 6)
|
|
39
|
+
assert_equal 1, r.getbit("foo", 7)
|
|
40
|
+
|
|
41
|
+
r.flushdb
|
|
42
|
+
|
|
43
|
+
# command pipelining
|
|
44
|
+
r.pipelined do
|
|
45
|
+
r.lpush "foo", "s1"
|
|
46
|
+
r.lpush "foo", "s2"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
assert 2 == r.llen("foo")
|
|
50
|
+
assert "s2" == r.lpop("foo")
|
|
51
|
+
assert "s1" == r.lpop("foo")
|
|
52
|
+
|
|
53
|
+
assert "OK" == r.client.call(:quit)
|
|
54
|
+
assert "PONG" == r.ping
|
|
55
|
+
|
|
56
|
+
EM.stop
|
|
57
|
+
end
|
data/test/test.conf
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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 "thread safety" do
|
|
10
|
+
redis = Redis.connect(OPTIONS.merge(:thread_safe => true))
|
|
11
|
+
|
|
12
|
+
redis.set "foo", 1
|
|
13
|
+
redis.set "bar", 2
|
|
14
|
+
|
|
15
|
+
sample = 100
|
|
16
|
+
|
|
17
|
+
t1 = Thread.new do
|
|
18
|
+
$foos = Array.new(sample) { redis.get "foo" }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
t2 = Thread.new do
|
|
22
|
+
$bars = Array.new(sample) { redis.get "bar" }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
t1.join
|
|
26
|
+
t2.join
|
|
27
|
+
|
|
28
|
+
assert_equal ["1"], $foos.uniq
|
|
29
|
+
assert_equal ["2"], $bars.uniq
|
|
30
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
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 "MULTI/DISCARD" do |r|
|
|
10
|
+
r.multi
|
|
11
|
+
|
|
12
|
+
assert "QUEUED" == r.set("foo", "1")
|
|
13
|
+
assert "QUEUED" == r.get("foo")
|
|
14
|
+
|
|
15
|
+
r.discard
|
|
16
|
+
|
|
17
|
+
assert nil == r.get("foo")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test "MULTI/EXEC with a block" do |r|
|
|
21
|
+
r.multi do |multi|
|
|
22
|
+
multi.set "foo", "s1"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
assert "s1" == r.get("foo")
|
|
26
|
+
|
|
27
|
+
assert_raise(RuntimeError) do
|
|
28
|
+
r.multi do |multi|
|
|
29
|
+
multi.set "bar", "s2"
|
|
30
|
+
raise "Some error"
|
|
31
|
+
multi.set "baz", "s3"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
assert nil == r.get("bar")
|
|
36
|
+
assert nil == r.get("baz")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test "Don't raise (and ignore) immediate error in MULTI/EXEC" do |r|
|
|
40
|
+
result = r.multi do |m|
|
|
41
|
+
m.set("foo", "s1")
|
|
42
|
+
m.unknown_command
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
assert 1 == result.size
|
|
46
|
+
assert "OK" == result.first
|
|
47
|
+
assert "s1" == r.get("foo")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test "Don't raise delayed error in MULTI/EXEC" do |r|
|
|
51
|
+
result = r.multi do |m|
|
|
52
|
+
m.set("foo", "s1")
|
|
53
|
+
m.incr("foo") # not an integer
|
|
54
|
+
m.lpush("foo", "value") # wrong kind of value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
assert result[1].message =~ /not an integer/i
|
|
58
|
+
assert result[2].message =~ /wrong kind of value/i
|
|
59
|
+
assert "s1" == r.get("foo")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test "MULTI with a block yielding the client" do |r|
|
|
63
|
+
r.multi do |multi|
|
|
64
|
+
multi.set "foo", "s1"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
assert "s1" == r.get("foo")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
test "WATCH with an unmodified key" do |r|
|
|
71
|
+
r.watch "foo"
|
|
72
|
+
r.multi do |multi|
|
|
73
|
+
multi.set "foo", "s1"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
assert "s1" == r.get("foo")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
test "WATCH with a modified key" do |r|
|
|
80
|
+
r.watch "foo"
|
|
81
|
+
r.set "foo", "s1"
|
|
82
|
+
res = r.multi do |multi|
|
|
83
|
+
multi.set "foo", "s2"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
assert nil == res
|
|
87
|
+
assert "s1" == r.get("foo")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
test "UNWATCH with a modified key" do |r|
|
|
91
|
+
r.watch "foo"
|
|
92
|
+
r.set "foo", "s1"
|
|
93
|
+
r.unwatch
|
|
94
|
+
r.multi do |multi|
|
|
95
|
+
multi.set "foo", "s2"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
assert "s2" == r.get("foo")
|
|
99
|
+
end
|
|
100
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
test "URL defaults to 127.0.0.1:6379" do
|
|
6
|
+
redis = Redis.connect
|
|
7
|
+
|
|
8
|
+
assert "127.0.0.1" == redis.client.host
|
|
9
|
+
assert 6379 == redis.client.port
|
|
10
|
+
assert 0 == redis.client.db
|
|
11
|
+
assert nil == redis.client.password
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "allows to pass in a URL" do
|
|
15
|
+
redis = Redis.connect :url => "redis://:secr3t@foo.com:999/2"
|
|
16
|
+
|
|
17
|
+
assert "foo.com" == redis.client.host
|
|
18
|
+
assert 999 == redis.client.port
|
|
19
|
+
assert 2 == redis.client.db
|
|
20
|
+
assert "secr3t" == redis.client.password
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "override URL if path option is passed" do
|
|
24
|
+
redis = Redis.connect :url => "redis://:secr3t@foo.com/foo:999/2", :path => "/tmp/redis.sock"
|
|
25
|
+
|
|
26
|
+
assert "/tmp/redis.sock" == redis.client.path
|
|
27
|
+
assert nil == redis.client.host
|
|
28
|
+
assert nil == redis.client.port
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test "overrides URL if another connection option is passed" do
|
|
32
|
+
redis = Redis.connect :url => "redis://:secr3t@foo.com:999/2", :port => 1000
|
|
33
|
+
|
|
34
|
+
assert "foo.com" == redis.client.host
|
|
35
|
+
assert 1000 == redis.client.port
|
|
36
|
+
assert 2 == redis.client.db
|
|
37
|
+
assert "secr3t" == redis.client.password
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
test "does not modify the passed options" do
|
|
41
|
+
options = { :url => "redis://:secr3t@foo.com:999/2" }
|
|
42
|
+
|
|
43
|
+
redis = Redis.connect(options)
|
|
44
|
+
|
|
45
|
+
assert({ :url => "redis://:secr3t@foo.com:999/2" } == options)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test "uses REDIS_URL over default if available" do
|
|
49
|
+
ENV["REDIS_URL"] = "redis://:secr3t@foo.com:999/2"
|
|
50
|
+
|
|
51
|
+
redis = Redis.connect
|
|
52
|
+
|
|
53
|
+
assert "foo.com" == redis.client.host
|
|
54
|
+
assert 999 == redis.client.port
|
|
55
|
+
assert 2 == redis.client.db
|
|
56
|
+
assert "secr3t" == redis.client.password
|
|
57
|
+
|
|
58
|
+
ENV.delete("REDIS_URL")
|
|
59
|
+
end
|
|
60
|
+
|