redis2-namespaced 3.0.7
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.order +170 -0
- data/.travis/Gemfile +11 -0
- data/.travis.yml +55 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +285 -0
- data/LICENSE +20 -0
- data/README.md +251 -0
- data/Rakefile +403 -0
- data/benchmarking/logging.rb +71 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -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 +37 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis2/client.rb +419 -0
- data/lib/redis2/connection/command_helper.rb +44 -0
- data/lib/redis2/connection/hiredis.rb +63 -0
- data/lib/redis2/connection/registry.rb +12 -0
- data/lib/redis2/connection/ruby.rb +322 -0
- data/lib/redis2/connection/synchrony.rb +124 -0
- data/lib/redis2/connection.rb +9 -0
- data/lib/redis2/distributed.rb +853 -0
- data/lib/redis2/errors.rb +40 -0
- data/lib/redis2/hash_ring.rb +131 -0
- data/lib/redis2/pipeline.rb +141 -0
- data/lib/redis2/subscribe.rb +83 -0
- data/lib/redis2/version.rb +3 -0
- data/lib/redis2.rb +2533 -0
- data/redis.gemspec +43 -0
- data/test/bitpos_test.rb +69 -0
- data/test/blocking_commands_test.rb +42 -0
- data/test/command_map_test.rb +30 -0
- data/test/commands_on_hashes_test.rb +21 -0
- data/test/commands_on_lists_test.rb +20 -0
- data/test/commands_on_sets_test.rb +77 -0
- data/test/commands_on_sorted_sets_test.rb +109 -0
- data/test/commands_on_strings_test.rb +101 -0
- data/test/commands_on_value_types_test.rb +131 -0
- data/test/connection_handling_test.rb +189 -0
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +46 -0
- data/test/distributed_commands_on_hashes_test.rb +10 -0
- data/test/distributed_commands_on_lists_test.rb +22 -0
- data/test/distributed_commands_on_sets_test.rb +83 -0
- data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
- data/test/distributed_commands_on_strings_test.rb +59 -0
- data/test/distributed_commands_on_value_types_test.rb +95 -0
- data/test/distributed_commands_requiring_clustering_test.rb +164 -0
- data/test/distributed_connection_handling_test.rb +23 -0
- data/test/distributed_internals_test.rb +70 -0
- data/test/distributed_key_tags_test.rb +52 -0
- data/test/distributed_persistence_control_commands_test.rb +26 -0
- data/test/distributed_publish_subscribe_test.rb +92 -0
- data/test/distributed_remote_server_control_commands_test.rb +66 -0
- data/test/distributed_scripting_test.rb +102 -0
- data/test/distributed_sorting_test.rb +20 -0
- data/test/distributed_test.rb +58 -0
- data/test/distributed_transactions_test.rb +32 -0
- data/test/encoding_test.rb +18 -0
- data/test/error_replies_test.rb +59 -0
- data/test/helper.rb +218 -0
- data/test/helper_test.rb +24 -0
- data/test/internals_test.rb +410 -0
- data/test/lint/blocking_commands.rb +150 -0
- data/test/lint/hashes.rb +162 -0
- data/test/lint/lists.rb +143 -0
- data/test/lint/sets.rb +125 -0
- data/test/lint/sorted_sets.rb +238 -0
- data/test/lint/strings.rb +260 -0
- data/test/lint/value_types.rb +122 -0
- data/test/persistence_control_commands_test.rb +26 -0
- data/test/pipelining_commands_test.rb +242 -0
- data/test/publish_subscribe_test.rb +210 -0
- data/test/remote_server_control_commands_test.rb +117 -0
- data/test/scanning_test.rb +413 -0
- data/test/scripting_test.rb +78 -0
- data/test/sorting_test.rb +59 -0
- data/test/support/connection/hiredis.rb +1 -0
- data/test/support/connection/ruby.rb +1 -0
- data/test/support/connection/synchrony.rb +17 -0
- data/test/support/redis_mock.rb +115 -0
- data/test/support/wire/synchrony.rb +24 -0
- data/test/support/wire/thread.rb +5 -0
- data/test/synchrony_driver.rb +88 -0
- data/test/test.conf +9 -0
- data/test/thread_safety_test.rb +32 -0
- data/test/transactions_test.rb +264 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +132 -0
- metadata +226 -0
@@ -0,0 +1,419 @@
|
|
1
|
+
require "redis2/errors"
|
2
|
+
require "socket"
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
class Redis2
|
6
|
+
class Client
|
7
|
+
|
8
|
+
DEFAULTS = {
|
9
|
+
:url => lambda { ENV["REDIS_URL"] },
|
10
|
+
:scheme => "redis",
|
11
|
+
:host => "127.0.0.1",
|
12
|
+
:port => 6379,
|
13
|
+
:path => nil,
|
14
|
+
:timeout => 5.0,
|
15
|
+
:password => nil,
|
16
|
+
:db => 0,
|
17
|
+
:driver => nil,
|
18
|
+
:id => nil,
|
19
|
+
:tcp_keepalive => 0
|
20
|
+
}
|
21
|
+
|
22
|
+
def options
|
23
|
+
Marshal.load(Marshal.dump(@options))
|
24
|
+
end
|
25
|
+
|
26
|
+
def scheme
|
27
|
+
@options[:scheme]
|
28
|
+
end
|
29
|
+
|
30
|
+
def host
|
31
|
+
@options[:host]
|
32
|
+
end
|
33
|
+
|
34
|
+
def port
|
35
|
+
@options[:port]
|
36
|
+
end
|
37
|
+
|
38
|
+
def path
|
39
|
+
@options[:path]
|
40
|
+
end
|
41
|
+
|
42
|
+
def timeout
|
43
|
+
@options[:timeout]
|
44
|
+
end
|
45
|
+
|
46
|
+
def password
|
47
|
+
@options[:password]
|
48
|
+
end
|
49
|
+
|
50
|
+
def db
|
51
|
+
@options[:db]
|
52
|
+
end
|
53
|
+
|
54
|
+
def db=(db)
|
55
|
+
@options[:db] = db.to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
def driver
|
59
|
+
@options[:driver]
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_accessor :logger
|
63
|
+
attr_reader :connection
|
64
|
+
attr_reader :command_map
|
65
|
+
|
66
|
+
def initialize(options = {})
|
67
|
+
@options = _parse_options(options)
|
68
|
+
@reconnect = true
|
69
|
+
@logger = @options[:logger]
|
70
|
+
@connection = nil
|
71
|
+
@command_map = {}
|
72
|
+
end
|
73
|
+
|
74
|
+
def connect
|
75
|
+
@pid = Process.pid
|
76
|
+
|
77
|
+
# Don't try to reconnect when the connection is fresh
|
78
|
+
with_reconnect(false) do
|
79
|
+
establish_connection
|
80
|
+
call [:auth, password] if password
|
81
|
+
call [:select, db] if db != 0
|
82
|
+
end
|
83
|
+
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def id
|
88
|
+
@options[:id] || "redis://#{location}/#{db}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def location
|
92
|
+
path || "#{host}:#{port}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def call(command, &block)
|
96
|
+
reply = process([command]) { read }
|
97
|
+
raise reply if reply.is_a?(CommandError)
|
98
|
+
|
99
|
+
if block
|
100
|
+
block.call(reply)
|
101
|
+
else
|
102
|
+
reply
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def call_loop(command)
|
107
|
+
error = nil
|
108
|
+
|
109
|
+
result = without_socket_timeout do
|
110
|
+
process([command]) do
|
111
|
+
loop do
|
112
|
+
reply = read
|
113
|
+
if reply.is_a?(CommandError)
|
114
|
+
error = reply
|
115
|
+
break
|
116
|
+
else
|
117
|
+
yield reply
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Raise error when previous block broke out of the loop.
|
124
|
+
raise error if error
|
125
|
+
|
126
|
+
# Result is set to the value that the provided block used to break.
|
127
|
+
result
|
128
|
+
end
|
129
|
+
|
130
|
+
def call_pipeline(pipeline)
|
131
|
+
with_reconnect pipeline.with_reconnect? do
|
132
|
+
begin
|
133
|
+
pipeline.finish(call_pipelined(pipeline.commands)).tap do
|
134
|
+
self.db = pipeline.db if pipeline.db
|
135
|
+
end
|
136
|
+
rescue ConnectionError => e
|
137
|
+
return nil if pipeline.shutdown?
|
138
|
+
# Assume the pipeline was sent in one piece, but execution of
|
139
|
+
# SHUTDOWN caused none of the replies for commands that were executed
|
140
|
+
# prior to it from coming back around.
|
141
|
+
raise e
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def call_pipelined(commands)
|
147
|
+
return [] if commands.empty?
|
148
|
+
|
149
|
+
# The method #ensure_connected (called from #process) reconnects once on
|
150
|
+
# I/O errors. To make an effort in making sure that commands are not
|
151
|
+
# executed more than once, only allow reconnection before the first reply
|
152
|
+
# has been read. When an error occurs after the first reply has been
|
153
|
+
# read, retrying would re-execute the entire pipeline, thus re-issuing
|
154
|
+
# already successfully executed commands. To circumvent this, don't retry
|
155
|
+
# after the first reply has been read successfully.
|
156
|
+
|
157
|
+
result = Array.new(commands.size)
|
158
|
+
reconnect = @reconnect
|
159
|
+
|
160
|
+
begin
|
161
|
+
process(commands) do
|
162
|
+
result[0] = read
|
163
|
+
|
164
|
+
@reconnect = false
|
165
|
+
|
166
|
+
(commands.size - 1).times do |i|
|
167
|
+
result[i + 1] = read
|
168
|
+
end
|
169
|
+
end
|
170
|
+
ensure
|
171
|
+
@reconnect = reconnect
|
172
|
+
end
|
173
|
+
|
174
|
+
result
|
175
|
+
end
|
176
|
+
|
177
|
+
def call_with_timeout(command, timeout, &blk)
|
178
|
+
with_socket_timeout(timeout) do
|
179
|
+
call(command, &blk)
|
180
|
+
end
|
181
|
+
rescue ConnectionError
|
182
|
+
retry
|
183
|
+
end
|
184
|
+
|
185
|
+
def call_without_timeout(command, &blk)
|
186
|
+
call_with_timeout(command, 0, &blk)
|
187
|
+
end
|
188
|
+
|
189
|
+
def process(commands)
|
190
|
+
logging(commands) do
|
191
|
+
ensure_connected do
|
192
|
+
commands.each do |command|
|
193
|
+
if command_map[command.first]
|
194
|
+
command = command.dup
|
195
|
+
command[0] = command_map[command.first]
|
196
|
+
end
|
197
|
+
|
198
|
+
write(command)
|
199
|
+
end
|
200
|
+
|
201
|
+
yield if block_given?
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def connected?
|
207
|
+
!! (connection && connection.connected?)
|
208
|
+
end
|
209
|
+
|
210
|
+
def disconnect
|
211
|
+
connection.disconnect if connected?
|
212
|
+
end
|
213
|
+
|
214
|
+
def reconnect
|
215
|
+
disconnect
|
216
|
+
connect
|
217
|
+
end
|
218
|
+
|
219
|
+
def io
|
220
|
+
yield
|
221
|
+
rescue TimeoutError => e1
|
222
|
+
# Add a message to the exception without destroying the original stack
|
223
|
+
e2 = TimeoutError.new("Connection timed out")
|
224
|
+
e2.set_backtrace(e1.backtrace)
|
225
|
+
raise e2
|
226
|
+
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL => e
|
227
|
+
raise ConnectionError, "Connection lost (%s)" % [e.class.name.split("::").last]
|
228
|
+
end
|
229
|
+
|
230
|
+
def read
|
231
|
+
io do
|
232
|
+
connection.read
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def write(command)
|
237
|
+
io do
|
238
|
+
connection.write(command)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def with_socket_timeout(timeout)
|
243
|
+
connect unless connected?
|
244
|
+
|
245
|
+
begin
|
246
|
+
connection.timeout = timeout
|
247
|
+
yield
|
248
|
+
ensure
|
249
|
+
connection.timeout = self.timeout if connected?
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def without_socket_timeout(&blk)
|
254
|
+
with_socket_timeout(0, &blk)
|
255
|
+
end
|
256
|
+
|
257
|
+
def with_reconnect(val=true)
|
258
|
+
begin
|
259
|
+
original, @reconnect = @reconnect, val
|
260
|
+
yield
|
261
|
+
ensure
|
262
|
+
@reconnect = original
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def without_reconnect(&blk)
|
267
|
+
with_reconnect(false, &blk)
|
268
|
+
end
|
269
|
+
|
270
|
+
protected
|
271
|
+
|
272
|
+
def logging(commands)
|
273
|
+
return yield unless @logger && @logger.debug?
|
274
|
+
|
275
|
+
begin
|
276
|
+
commands.each do |name, *args|
|
277
|
+
@logger.debug("Redis2 >> #{name.to_s.upcase} #{args.map(&:to_s).join(" ")}")
|
278
|
+
end
|
279
|
+
|
280
|
+
t1 = Time.now
|
281
|
+
yield
|
282
|
+
ensure
|
283
|
+
@logger.debug("Redis2 >> %0.2fms" % ((Time.now - t1) * 1000)) if t1
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def establish_connection
|
288
|
+
@connection = @options[:driver].connect(@options.dup)
|
289
|
+
|
290
|
+
rescue TimeoutError
|
291
|
+
raise CannotConnectError, "Timed out connecting to Redis2 on #{location}"
|
292
|
+
rescue Errno::ECONNREFUSED
|
293
|
+
raise CannotConnectError, "Error connecting to Redis2 on #{location} (ECONNREFUSED)"
|
294
|
+
end
|
295
|
+
|
296
|
+
def ensure_connected
|
297
|
+
tries = 0
|
298
|
+
|
299
|
+
begin
|
300
|
+
tries += 1
|
301
|
+
|
302
|
+
if connected?
|
303
|
+
if Process.pid != @pid
|
304
|
+
raise InheritedError,
|
305
|
+
"Tried to use a connection from a child process without reconnecting. " +
|
306
|
+
"You need to reconnect to Redis2 after forking."
|
307
|
+
end
|
308
|
+
else
|
309
|
+
connect
|
310
|
+
end
|
311
|
+
|
312
|
+
yield
|
313
|
+
rescue ConnectionError, InheritedError
|
314
|
+
disconnect
|
315
|
+
|
316
|
+
if tries < 2 && @reconnect
|
317
|
+
retry
|
318
|
+
else
|
319
|
+
raise
|
320
|
+
end
|
321
|
+
rescue Exception
|
322
|
+
disconnect
|
323
|
+
raise
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def _parse_options(options)
|
328
|
+
defaults = DEFAULTS.dup
|
329
|
+
options = options.dup
|
330
|
+
|
331
|
+
defaults.keys.each do |key|
|
332
|
+
# Fill in defaults if needed
|
333
|
+
if defaults[key].respond_to?(:call)
|
334
|
+
defaults[key] = defaults[key].call
|
335
|
+
end
|
336
|
+
|
337
|
+
# Symbolize only keys that are needed
|
338
|
+
options[key] = options[key.to_s] if options.has_key?(key.to_s)
|
339
|
+
end
|
340
|
+
|
341
|
+
url = options[:url] || defaults[:url]
|
342
|
+
|
343
|
+
# Override defaults from URL if given
|
344
|
+
if url
|
345
|
+
require "uri"
|
346
|
+
|
347
|
+
uri = URI(url)
|
348
|
+
|
349
|
+
if uri.scheme == "unix"
|
350
|
+
defaults[:path] = uri.path
|
351
|
+
else
|
352
|
+
# Require the URL to have at least a host
|
353
|
+
raise ArgumentError, "invalid url" unless uri.host
|
354
|
+
|
355
|
+
defaults[:scheme] = uri.scheme
|
356
|
+
defaults[:host] = uri.host
|
357
|
+
defaults[:port] = uri.port if uri.port
|
358
|
+
defaults[:password] = CGI.unescape(uri.password) if uri.password
|
359
|
+
defaults[:db] = uri.path[1..-1].to_i if uri.path
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Use default when option is not specified or nil
|
364
|
+
defaults.keys.each do |key|
|
365
|
+
options[key] ||= defaults[key]
|
366
|
+
end
|
367
|
+
|
368
|
+
if options[:path]
|
369
|
+
options[:scheme] = "unix"
|
370
|
+
options.delete(:host)
|
371
|
+
options.delete(:port)
|
372
|
+
else
|
373
|
+
options[:host] = options[:host].to_s
|
374
|
+
options[:port] = options[:port].to_i
|
375
|
+
end
|
376
|
+
|
377
|
+
options[:timeout] = options[:timeout].to_f
|
378
|
+
options[:db] = options[:db].to_i
|
379
|
+
options[:driver] = _parse_driver(options[:driver]) || Connection.drivers.last
|
380
|
+
|
381
|
+
case options[:tcp_keepalive]
|
382
|
+
when Hash
|
383
|
+
[:time, :intvl, :probes].each do |key|
|
384
|
+
unless options[:tcp_keepalive][key].is_a?(Fixnum)
|
385
|
+
raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
when Fixnum
|
390
|
+
if options[:tcp_keepalive] >= 60
|
391
|
+
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 20, :intvl => 10, :probes => 2}
|
392
|
+
|
393
|
+
elsif options[:tcp_keepalive] >= 30
|
394
|
+
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 10, :intvl => 5, :probes => 2}
|
395
|
+
|
396
|
+
elsif options[:tcp_keepalive] >= 5
|
397
|
+
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 2, :intvl => 2, :probes => 1}
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
options
|
402
|
+
end
|
403
|
+
|
404
|
+
def _parse_driver(driver)
|
405
|
+
driver = driver.to_s if driver.is_a?(Symbol)
|
406
|
+
|
407
|
+
if driver.kind_of?(String)
|
408
|
+
begin
|
409
|
+
require "redis2/connection/#{driver}"
|
410
|
+
driver = Connection.const_get(driver.capitalize)
|
411
|
+
rescue LoadError, NameError
|
412
|
+
raise RuntimeError, "Cannot load driver #{driver.inspect}"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
driver
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Redis2
|
2
|
+
module Connection
|
3
|
+
module CommandHelper
|
4
|
+
|
5
|
+
COMMAND_DELIMITER = "\r\n"
|
6
|
+
|
7
|
+
def build_command(args)
|
8
|
+
command = [nil]
|
9
|
+
|
10
|
+
args.each do |i|
|
11
|
+
if i.is_a? Array
|
12
|
+
i.each do |j|
|
13
|
+
j = j.to_s
|
14
|
+
command << "$#{j.bytesize}"
|
15
|
+
command << j
|
16
|
+
end
|
17
|
+
else
|
18
|
+
i = i.to_s
|
19
|
+
command << "$#{i.bytesize}"
|
20
|
+
command << i
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
command[0] = "*#{(command.length - 1) / 2}"
|
25
|
+
|
26
|
+
# Trailing delimiter
|
27
|
+
command << ""
|
28
|
+
command.join(COMMAND_DELIMITER)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
if defined?(Encoding::default_external)
|
34
|
+
def encode(string)
|
35
|
+
string.force_encoding(Encoding::default_external)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
def encode(string)
|
39
|
+
string
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "redis2/connection/registry"
|
2
|
+
require "redis2/errors"
|
3
|
+
require "hiredis/connection"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
class Redis2
|
7
|
+
module Connection
|
8
|
+
class Hiredis
|
9
|
+
|
10
|
+
def self.connect(config)
|
11
|
+
connection = ::Hiredis::Connection.new
|
12
|
+
|
13
|
+
if config[:scheme] == "unix"
|
14
|
+
connection.connect_unix(config[:path], Integer(config[:timeout] * 1_000_000))
|
15
|
+
else
|
16
|
+
connection.connect(config[:host], config[:port], Integer(config[:timeout] * 1_000_000))
|
17
|
+
end
|
18
|
+
|
19
|
+
instance = new(connection)
|
20
|
+
instance.timeout = config[:timeout]
|
21
|
+
instance
|
22
|
+
rescue Errno::ETIMEDOUT
|
23
|
+
raise TimeoutError
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(connection)
|
27
|
+
@connection = connection
|
28
|
+
end
|
29
|
+
|
30
|
+
def connected?
|
31
|
+
@connection && @connection.connected?
|
32
|
+
end
|
33
|
+
|
34
|
+
def timeout=(timeout)
|
35
|
+
# Hiredis works with microsecond timeouts
|
36
|
+
@connection.timeout = Integer(timeout * 1_000_000)
|
37
|
+
end
|
38
|
+
|
39
|
+
def disconnect
|
40
|
+
@connection.disconnect
|
41
|
+
@connection = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def write(command)
|
45
|
+
@connection.write(command.flatten(1))
|
46
|
+
rescue Errno::EAGAIN
|
47
|
+
raise TimeoutError
|
48
|
+
end
|
49
|
+
|
50
|
+
def read
|
51
|
+
reply = @connection.read
|
52
|
+
reply = CommandError.new(reply.message) if reply.is_a?(RuntimeError)
|
53
|
+
reply
|
54
|
+
rescue Errno::EAGAIN
|
55
|
+
raise TimeoutError
|
56
|
+
rescue RuntimeError => err
|
57
|
+
raise ProtocolError.new(err.message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
Redis2::Connection.drivers << Redis2::Connection::Hiredis
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Redis2
|
2
|
+
module Connection
|
3
|
+
|
4
|
+
# Store a list of loaded connection drivers in the Connection module.
|
5
|
+
# Redis2::Client uses the last required driver by default, and will be aware
|
6
|
+
# of the loaded connection drivers if the user chooses to override the
|
7
|
+
# default connection driver.
|
8
|
+
def self.drivers
|
9
|
+
@drivers ||= []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|