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 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.13"
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 'redis/client'
673
- require 'redis/pipeline'
674
- require 'redis/subscribe'
675
- require 'redis/compat'
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
- @sock = nil
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
- @sock
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
- @sock.write(join_commands(commands))
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
- !! @sock
69
+ connection.connected?
72
70
  end
73
71
 
74
72
  def disconnect
75
- return unless connected?
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
- reply_type = @sock.read(1)
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
- format_reply(reply_type, @sock.gets)
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
- @sock = TCPSocket.new(host, port)
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
- secs = Integer(timeout)
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: false
4
+ prerelease: true
5
5
  segments:
6
6
  - 2
7
+ - 1
7
8
  - 0
8
- - 13
9
- version: 2.0.13
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-01 00:00:00 -03:00
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
- - 0
72
- version: "0"
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