redis 2.0.13 → 2.1.0.beta

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/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