redis 2.0.13 → 2.1.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +9 -0
- data/lib/redis.rb +6 -5
- data/lib/redis/client.rb +17 -120
- data/lib/redis/connection.rb +134 -0
- metadata +11 -8
- data/lib/redis/url.rb +0 -69
data/README.markdown
CHANGED
@@ -111,6 +111,15 @@ You can use `MULTI/EXEC` to run arbitrary commands in an atomic fashion:
|
|
111
111
|
redis.incr "baz"
|
112
112
|
end
|
113
113
|
|
114
|
+
## Multithreaded Operation
|
115
|
+
|
116
|
+
To use redis safely in a multithreaded environment, be sure to initialize the client with :thread_safe=>true
|
117
|
+
|
118
|
+
Redis.new(:thread_safe=>true)
|
119
|
+
|
120
|
+
See the tests and benchmarks for examples.
|
121
|
+
|
122
|
+
|
114
123
|
## More info
|
115
124
|
|
116
125
|
Check the [Redis Command Reference](http://code.google.com/p/redis/wiki/CommandReference) or check the tests to find out how to use this client.
|
data/lib/redis.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'socket'
|
2
2
|
|
3
3
|
class Redis
|
4
|
-
VERSION = "2.0.
|
4
|
+
VERSION = "2.1.0.beta"
|
5
5
|
|
6
6
|
class ProtocolError < RuntimeError
|
7
7
|
def initialize(reply_type)
|
@@ -669,7 +669,8 @@ private
|
|
669
669
|
|
670
670
|
end
|
671
671
|
|
672
|
-
require
|
673
|
-
require
|
674
|
-
require
|
675
|
-
require
|
672
|
+
require "redis/connection" unless defined?(Redis::Connection)
|
673
|
+
require "redis/client"
|
674
|
+
require "redis/pipeline"
|
675
|
+
require "redis/subscribe"
|
676
|
+
require "redis/compat"
|
data/lib/redis/client.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
class Redis
|
2
2
|
class Client
|
3
|
-
MINUS = "-".freeze
|
4
|
-
PLUS = "+".freeze
|
5
|
-
COLON = ":".freeze
|
6
|
-
DOLLAR = "$".freeze
|
7
|
-
ASTERISK = "*".freeze
|
8
|
-
|
9
3
|
attr_accessor :db, :host, :port, :password, :logger
|
10
4
|
attr :timeout
|
5
|
+
attr :connection
|
11
6
|
|
12
7
|
def initialize(options = {})
|
13
8
|
@host = options[:host] || "127.0.0.1"
|
@@ -16,14 +11,14 @@ class Redis
|
|
16
11
|
@timeout = (options[:timeout] || 5).to_i
|
17
12
|
@password = options[:password]
|
18
13
|
@logger = options[:logger]
|
19
|
-
@
|
14
|
+
@connection = Connection.new
|
20
15
|
end
|
21
16
|
|
22
17
|
def connect
|
23
18
|
connect_to(@host, @port)
|
24
19
|
call(:auth, @password) if @password
|
25
20
|
call(:select, @db) if @db != 0
|
26
|
-
|
21
|
+
self
|
27
22
|
end
|
28
23
|
|
29
24
|
def id
|
@@ -61,25 +56,21 @@ class Redis
|
|
61
56
|
def process(*commands)
|
62
57
|
logging(commands) do
|
63
58
|
ensure_connected do
|
64
|
-
|
59
|
+
commands.each do |command|
|
60
|
+
connection.write(command)
|
61
|
+
end
|
62
|
+
|
65
63
|
yield if block_given?
|
66
64
|
end
|
67
65
|
end
|
68
66
|
end
|
69
67
|
|
70
68
|
def connected?
|
71
|
-
|
69
|
+
connection.connected?
|
72
70
|
end
|
73
71
|
|
74
72
|
def disconnect
|
75
|
-
|
76
|
-
|
77
|
-
begin
|
78
|
-
@sock.close
|
79
|
-
rescue
|
80
|
-
ensure
|
81
|
-
@sock = nil
|
82
|
-
end
|
73
|
+
connection.disconnect if connection.connected?
|
83
74
|
end
|
84
75
|
|
85
76
|
def reconnect
|
@@ -88,23 +79,20 @@ class Redis
|
|
88
79
|
end
|
89
80
|
|
90
81
|
def read
|
91
|
-
# We read the first byte using read() mainly because gets() is
|
92
|
-
# immune to raw socket timeouts.
|
93
82
|
begin
|
94
|
-
|
95
|
-
rescue Errno::EAGAIN
|
83
|
+
connection.read
|
96
84
|
|
85
|
+
rescue Errno::EAGAIN
|
97
86
|
# We want to make sure it reconnects on the next command after the
|
98
87
|
# timeout. Otherwise the server may reply in the meantime leaving
|
99
88
|
# the protocol in a desync status.
|
100
|
-
disconnect
|
89
|
+
connection.disconnect
|
101
90
|
|
102
91
|
raise Errno::EAGAIN, "Timeout reading from the socket"
|
103
|
-
end
|
104
|
-
|
105
|
-
raise Errno::ECONNRESET, "Connection lost" unless reply_type
|
106
92
|
|
107
|
-
|
93
|
+
rescue Errno::ECONNRESET
|
94
|
+
raise Errno::ECONNRESET, "Connection lost"
|
95
|
+
end
|
108
96
|
end
|
109
97
|
|
110
98
|
def without_socket_timeout
|
@@ -120,83 +108,12 @@ class Redis
|
|
120
108
|
|
121
109
|
protected
|
122
110
|
|
123
|
-
def build_command(name, *args)
|
124
|
-
command = []
|
125
|
-
command << "*#{args.size + 1}"
|
126
|
-
command << "$#{string_size name}"
|
127
|
-
command << name
|
128
|
-
|
129
|
-
args.each do |arg|
|
130
|
-
arg = arg.to_s
|
131
|
-
command << "$#{string_size arg}"
|
132
|
-
command << arg
|
133
|
-
end
|
134
|
-
|
135
|
-
command
|
136
|
-
end
|
137
|
-
|
138
111
|
def deprecated(old, new = nil, trace = caller[0])
|
139
112
|
message = "The method #{old} is deprecated and will be removed in 2.0"
|
140
113
|
message << " - use #{new} instead" if new
|
141
114
|
Redis.deprecate(message, trace)
|
142
115
|
end
|
143
116
|
|
144
|
-
COMMAND_DELIMITER = "\r\n"
|
145
|
-
|
146
|
-
def join_commands(commands)
|
147
|
-
commands.map do |command|
|
148
|
-
build_command(*command).join(COMMAND_DELIMITER) + COMMAND_DELIMITER
|
149
|
-
end.join(nil)
|
150
|
-
end
|
151
|
-
|
152
|
-
if "".respond_to?(:bytesize)
|
153
|
-
def string_size(string)
|
154
|
-
string.to_s.bytesize
|
155
|
-
end
|
156
|
-
else
|
157
|
-
def string_size(string)
|
158
|
-
string.to_s.size
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def format_reply(reply_type, line)
|
163
|
-
case reply_type
|
164
|
-
when MINUS then format_error_reply(line)
|
165
|
-
when PLUS then format_status_reply(line)
|
166
|
-
when COLON then format_integer_reply(line)
|
167
|
-
when DOLLAR then format_bulk_reply(line)
|
168
|
-
when ASTERISK then format_multi_bulk_reply(line)
|
169
|
-
else raise ProtocolError.new(reply_type)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
def format_error_reply(line)
|
174
|
-
raise "-" + line.strip
|
175
|
-
end
|
176
|
-
|
177
|
-
def format_status_reply(line)
|
178
|
-
line.strip
|
179
|
-
end
|
180
|
-
|
181
|
-
def format_integer_reply(line)
|
182
|
-
line.to_i
|
183
|
-
end
|
184
|
-
|
185
|
-
def format_bulk_reply(line)
|
186
|
-
bulklen = line.to_i
|
187
|
-
return if bulklen == -1
|
188
|
-
reply = encode(@sock.read(bulklen))
|
189
|
-
@sock.read(2) # Discard CRLF.
|
190
|
-
reply
|
191
|
-
end
|
192
|
-
|
193
|
-
def format_multi_bulk_reply(line)
|
194
|
-
n = line.to_i
|
195
|
-
return if n == -1
|
196
|
-
|
197
|
-
Array.new(n) { read }
|
198
|
-
end
|
199
|
-
|
200
117
|
def logging(commands)
|
201
118
|
return yield unless @logger && @logger.debug?
|
202
119
|
|
@@ -214,11 +131,9 @@ class Redis
|
|
214
131
|
|
215
132
|
def connect_to(host, port)
|
216
133
|
with_timeout(@timeout) do
|
217
|
-
|
134
|
+
connection.connect(host, port)
|
218
135
|
end
|
219
136
|
|
220
|
-
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
221
|
-
|
222
137
|
# If the timeout is set we set the low level socket options in order
|
223
138
|
# to make sure a blocking read will return after the specified number
|
224
139
|
# of seconds. This hack is from memcached ruby client.
|
@@ -229,15 +144,7 @@ class Redis
|
|
229
144
|
end
|
230
145
|
|
231
146
|
def timeout=(timeout)
|
232
|
-
|
233
|
-
usecs = Integer((timeout - secs) * 1_000_000)
|
234
|
-
optval = [secs, usecs].pack("l_2")
|
235
|
-
|
236
|
-
begin
|
237
|
-
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
238
|
-
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
239
|
-
rescue Errno::ENOPROTOOPT
|
240
|
-
end
|
147
|
+
connection.timeout = Integer(timeout * 1_000_000)
|
241
148
|
end
|
242
149
|
|
243
150
|
def ensure_connected
|
@@ -290,15 +197,5 @@ class Redis
|
|
290
197
|
Timeout.timeout(seconds, &block)
|
291
198
|
end
|
292
199
|
end
|
293
|
-
|
294
|
-
if defined?(Encoding::default_external)
|
295
|
-
def encode(string)
|
296
|
-
string.force_encoding(Encoding::default_external)
|
297
|
-
end
|
298
|
-
else
|
299
|
-
def encode(string)
|
300
|
-
string
|
301
|
-
end
|
302
|
-
end
|
303
200
|
end
|
304
201
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
class Redis
|
2
|
+
class Connection
|
3
|
+
MINUS = "-".freeze
|
4
|
+
PLUS = "+".freeze
|
5
|
+
COLON = ":".freeze
|
6
|
+
DOLLAR = "$".freeze
|
7
|
+
ASTERISK = "*".freeze
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@sock = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def connected?
|
14
|
+
!! @sock
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect(host, port)
|
18
|
+
@sock = TCPSocket.new(host, port)
|
19
|
+
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def disconnect
|
23
|
+
@sock.close
|
24
|
+
rescue
|
25
|
+
ensure
|
26
|
+
@sock = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def timeout=(usecs)
|
30
|
+
secs = Integer(usecs / 1_000_000)
|
31
|
+
usecs = Integer(usecs - (secs * 1_000_000)) # 0 - 999_999
|
32
|
+
|
33
|
+
optval = [secs, usecs].pack("l_2")
|
34
|
+
|
35
|
+
begin
|
36
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
37
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
38
|
+
rescue Errno::ENOPROTOOPT
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
COMMAND_DELIMITER = "\r\n"
|
43
|
+
|
44
|
+
def write(command)
|
45
|
+
@sock.write(build_command(*command).join(COMMAND_DELIMITER))
|
46
|
+
@sock.write(COMMAND_DELIMITER)
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_command(name, *args)
|
50
|
+
command = []
|
51
|
+
command << "*#{args.size + 1}"
|
52
|
+
command << "$#{string_size name}"
|
53
|
+
command << name
|
54
|
+
|
55
|
+
args.each do |arg|
|
56
|
+
arg = arg.to_s
|
57
|
+
command << "$#{string_size arg}"
|
58
|
+
command << arg
|
59
|
+
end
|
60
|
+
|
61
|
+
command
|
62
|
+
end
|
63
|
+
|
64
|
+
def read
|
65
|
+
# We read the first byte using read() mainly because gets() is
|
66
|
+
# immune to raw socket timeouts.
|
67
|
+
reply_type = @sock.read(1)
|
68
|
+
|
69
|
+
raise Errno::ECONNRESET unless reply_type
|
70
|
+
|
71
|
+
format_reply(reply_type, @sock.gets)
|
72
|
+
end
|
73
|
+
|
74
|
+
def format_reply(reply_type, line)
|
75
|
+
case reply_type
|
76
|
+
when MINUS then format_error_reply(line)
|
77
|
+
when PLUS then format_status_reply(line)
|
78
|
+
when COLON then format_integer_reply(line)
|
79
|
+
when DOLLAR then format_bulk_reply(line)
|
80
|
+
when ASTERISK then format_multi_bulk_reply(line)
|
81
|
+
else raise ProtocolError.new(reply_type)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def format_error_reply(line)
|
86
|
+
raise "-" + line.strip
|
87
|
+
end
|
88
|
+
|
89
|
+
def format_status_reply(line)
|
90
|
+
line.strip
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_integer_reply(line)
|
94
|
+
line.to_i
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_bulk_reply(line)
|
98
|
+
bulklen = line.to_i
|
99
|
+
return if bulklen == -1
|
100
|
+
reply = encode(@sock.read(bulklen))
|
101
|
+
@sock.read(2) # Discard CRLF.
|
102
|
+
reply
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_multi_bulk_reply(line)
|
106
|
+
n = line.to_i
|
107
|
+
return if n == -1
|
108
|
+
|
109
|
+
Array.new(n) { read }
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
if "".respond_to?(:bytesize)
|
115
|
+
def string_size(string)
|
116
|
+
string.to_s.bytesize
|
117
|
+
end
|
118
|
+
else
|
119
|
+
def string_size(string)
|
120
|
+
string.to_s.size
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if defined?(Encoding::default_external)
|
125
|
+
def encode(string)
|
126
|
+
string.force_encoding(Encoding::default_external)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
def encode(string)
|
130
|
+
string
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: true
|
5
5
|
segments:
|
6
6
|
- 2
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 2.0.
|
9
|
+
- beta
|
10
|
+
version: 2.1.0.beta
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Ezra Zygmuntowicz
|
@@ -21,7 +22,7 @@ autorequire: redis
|
|
21
22
|
bindir: bin
|
22
23
|
cert_chain: []
|
23
24
|
|
24
|
-
date: 2010-11-
|
25
|
+
date: 2010-11-04 00:00:00 -03:00
|
25
26
|
default_executable:
|
26
27
|
dependencies: []
|
27
28
|
|
@@ -39,11 +40,11 @@ files:
|
|
39
40
|
- Rakefile
|
40
41
|
- lib/redis/client.rb
|
41
42
|
- lib/redis/compat.rb
|
43
|
+
- lib/redis/connection.rb
|
42
44
|
- lib/redis/distributed.rb
|
43
45
|
- lib/redis/hash_ring.rb
|
44
46
|
- lib/redis/pipeline.rb
|
45
47
|
- lib/redis/subscribe.rb
|
46
|
-
- lib/redis/url.rb
|
47
48
|
- lib/redis.rb
|
48
49
|
has_rdoc: true
|
49
50
|
homepage: http://github.com/ezmobius/redis-rb
|
@@ -65,11 +66,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
67
|
none: false
|
67
68
|
requirements:
|
68
|
-
- - "
|
69
|
+
- - ">"
|
69
70
|
- !ruby/object:Gem::Version
|
70
71
|
segments:
|
71
|
-
-
|
72
|
-
|
72
|
+
- 1
|
73
|
+
- 3
|
74
|
+
- 1
|
75
|
+
version: 1.3.1
|
73
76
|
requirements: []
|
74
77
|
|
75
78
|
rubyforge_project:
|
data/lib/redis/url.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
require "uri/generic"
|
2
|
-
|
3
|
-
module URI
|
4
|
-
class Redis < Generic
|
5
|
-
DEFAULT_PORT = 6379
|
6
|
-
|
7
|
-
COMPONENT = [:scheme, :password, :host, :port, :db].freeze
|
8
|
-
|
9
|
-
def db
|
10
|
-
path[1..-1].to_i
|
11
|
-
end
|
12
|
-
|
13
|
-
alias password user
|
14
|
-
|
15
|
-
protected
|
16
|
-
def check_path(value)
|
17
|
-
if super(value)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
@@schemes["REDIS"] = Redis
|
23
|
-
end
|
24
|
-
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
25
|
-
require "redis/url"
|
26
|
-
|
27
|
-
class RedisURLTest < Test::Unit::TestCase
|
28
|
-
test "default values" do
|
29
|
-
uri = URI.parse("redis://localhost")
|
30
|
-
|
31
|
-
assert_equal "localhost", uri.host
|
32
|
-
assert_equal 6379, uri.port
|
33
|
-
assert_equal 0, uri.db
|
34
|
-
assert_equal nil, uri.password
|
35
|
-
end
|
36
|
-
|
37
|
-
test "password" do
|
38
|
-
uri = URI.parse("redis://secret@localhost")
|
39
|
-
|
40
|
-
assert_equal "localhost", uri.host
|
41
|
-
assert_equal 6379, uri.port
|
42
|
-
assert_equal 0, uri.db
|
43
|
-
assert_equal "secret", uri.password
|
44
|
-
end
|
45
|
-
|
46
|
-
test "db number" do
|
47
|
-
uri = URI.parse("redis://secret@localhost/15")
|
48
|
-
|
49
|
-
assert_equal "localhost", uri.host
|
50
|
-
assert_equal 6379, uri.port
|
51
|
-
assert_equal 15, uri.db
|
52
|
-
assert_equal "secret", uri.password
|
53
|
-
end
|
54
|
-
|
55
|
-
test "port" do
|
56
|
-
uri = URI.parse("redis://localhost:6380")
|
57
|
-
|
58
|
-
assert_equal "localhost", uri.host
|
59
|
-
assert_equal 6380, uri.port
|
60
|
-
assert_equal 0, uri.db
|
61
|
-
assert_equal nil, uri.password
|
62
|
-
end
|
63
|
-
|
64
|
-
test "to_s" do
|
65
|
-
uri = URI.parse("redis://secret@localhost:6380/15")
|
66
|
-
|
67
|
-
assert_equal "redis://secret@localhost:6380/15", uri.to_s
|
68
|
-
end
|
69
|
-
end
|