em-hiredis 0.1.1 → 0.2.0
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/CHANGELOG.md +19 -0
- data/README.md +80 -27
- data/examples/getting_started.rb +14 -0
- data/examples/pubsub_basics.rb +24 -0
- data/examples/pubsub_more.rb +51 -0
- data/examples/pubsub_raw.rb +25 -0
- data/lib/em-hiredis.rb +23 -3
- data/lib/em-hiredis/base_client.rb +200 -0
- data/lib/em-hiredis/client.rb +40 -155
- data/lib/em-hiredis/connection.rb +12 -4
- data/lib/em-hiredis/lock.rb +106 -0
- data/lib/em-hiredis/persistent_lock.rb +77 -0
- data/lib/em-hiredis/pubsub_client.rb +187 -0
- data/lib/em-hiredis/version.rb +1 -1
- data/spec/base_client_spec.rb +116 -0
- data/spec/connection_spec.rb +1 -1
- data/spec/live_redis_protocol_spec.rb +1 -1
- data/spec/pubsub_spec.rb +314 -0
- data/spec/redis_commands_spec.rb +29 -8
- data/spec/spec_helper.rb +3 -0
- data/spec/support/connection_helper.rb +5 -3
- data/spec/url_param_spec.rb +3 -3
- metadata +45 -22
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e19e2546bb52eb3e8f9f4a969d5666cd04eb0f39
|
4
|
+
data.tar.gz: 80435303e1cf652b489855c8e8f83228e614918e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aeea4ec51cc1a5ca3f0cf1ccecc5f00513d7840f1af34460ee5c19768e14d832d3460ada718927ac92c467d95a68f9e5f170bb318ea2b19510ef9b9c01de8158
|
7
|
+
data.tar.gz: 15d493eb81dfe7d044c8a55d25b3b269dd2af36d9d129fd5bfbd9d598f30d828a8ca7643a7a7f82a8f264b21d4ca8a2d5f5e43f25c48029792ff8024ba1a8062
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.2.0 (2013-04-05)
|
4
|
+
|
5
|
+
[NEW] Richer interface for pubsub (accessible via `client.pubsub`). See example in `examples/pubsub.rb`.
|
6
|
+
|
7
|
+
[NEW] Better failure handling:
|
8
|
+
|
9
|
+
* Clients now emit the following events: connected, reconnected, disconnected, reconnect_failed (passes the number of consecutive failures)
|
10
|
+
* Client is considered failed after 4 consecutive failures
|
11
|
+
* Fails all queued commands when client failed
|
12
|
+
* Can now reconfiure and reconnect an exising client
|
13
|
+
* Reconnect timeout can be configured (defaults to 0.5s)
|
14
|
+
|
15
|
+
[NEW] Added `EM::Hiredis::Lock` and `EM::Hiredis::PersistentLock`
|
16
|
+
|
17
|
+
[CHANGE] When a redis command fails, the errback is now always passed an `EM::Hiredis::Error`.
|
18
|
+
|
19
|
+
[FIX] Fixed info parsing for Redis 2.6
|
data/README.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# em-hiredis
|
2
|
+
|
3
|
+
## What
|
4
|
+
|
5
|
+
A Redis client for EventMachine designed to be fast and simple.
|
6
|
+
|
7
|
+
## Why
|
8
|
+
|
9
|
+
I wanted a client which:
|
10
|
+
|
11
|
+
* used the C hiredis library to parse redis replies
|
12
|
+
* had a convenient API for pubsub
|
13
|
+
* exposed the state of the underlying redis connections so that custom failover logic could be written outside the library
|
14
|
+
|
15
|
+
Also, <https://github.com/madsimian/em-redis> is no longer maintained.
|
16
|
+
|
17
|
+
## Getting started
|
3
18
|
|
4
19
|
Connect to redis:
|
5
20
|
|
@@ -10,11 +25,9 @@ Or, connect to redis with a redis URL (for a different host, port, password, DB)
|
|
10
25
|
|
11
26
|
redis = EM::Hiredis.connect("redis://:secretpassword@example.com:9000/4")
|
12
27
|
|
13
|
-
|
14
|
-
|
15
|
-
redis.callback { puts "Redis now connected" }
|
28
|
+
Commands may be sent immediately. Any commands sent while connecting to redis will be queued.
|
16
29
|
|
17
|
-
All redis commands are available without any remapping of names
|
30
|
+
All redis commands are available without any remapping of names, and return a deferrable
|
18
31
|
|
19
32
|
redis.set('foo', 'bar').callback {
|
20
33
|
redis.get('foo').callback { |value|
|
@@ -22,40 +35,54 @@ All redis commands are available without any remapping of names
|
|
22
35
|
}
|
23
36
|
}
|
24
37
|
|
38
|
+
If redis replies with an error (for example you called a hash operation against a set or the database is full), or if the redis connection disconnects before the command returns, the deferrable will fail.
|
39
|
+
|
40
|
+
redis.sadd('aset', 'member').callback {
|
41
|
+
response_deferrable = redis.hget('aset', 'member')
|
42
|
+
response_deferrable.errback { |e|
|
43
|
+
p e # => #<EventMachine::Hiredis::RedisError: Error reply from redis (wrapped in redis_error)>
|
44
|
+
p e.redis_error # => #<RuntimeError: ERR Operation against a key holding the wrong kind of value>
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
25
48
|
As a shortcut, if you're only interested in binding to the success case you can simply provide a block to any command
|
26
49
|
|
27
50
|
redis.get('foo') { |value|
|
28
51
|
p [:returned, value]
|
29
52
|
}
|
30
53
|
|
31
|
-
|
32
|
-
----------------
|
54
|
+
## Understanding the state of the connection
|
33
55
|
|
34
|
-
|
56
|
+
When a connection to redis server closes, a `:disconnected` event will be emitted and the connection will be immediately reconnect. If the connection reconnects a `:connected` event will be emitted.
|
35
57
|
|
36
|
-
|
37
|
-
response_deferrable = redis.hget('aset', 'member')
|
38
|
-
response_deferrable.errback { |e|
|
39
|
-
p e # => #<RuntimeError: ERR Operation against a key holding the wrong kind of value>
|
40
|
-
}
|
41
|
-
}
|
58
|
+
If a reconnect fails to connect, a `:reconnect_failed` event will be emitted (rather than `:disconnected`) with the number of consecutive failures, and the connection will be retried after a timeout (defaults to 0.5s, can be set via `EM::Hiredis.reconnect_timeout=`).
|
42
59
|
|
43
|
-
|
44
|
-
------
|
60
|
+
If a client fails to reconnect 4 consecutive times then a `:failed` event will be emitted, and any queued redis commands will be failed (otherwise they would be queued forever waiting for a reconnect).
|
45
61
|
|
46
|
-
|
62
|
+
## Pubsub
|
47
63
|
|
64
|
+
The way pubsub works in redis is that once a subscribe has been made on a connection, it's only possible to send (p)subscribe or (p)unsubscribe commands on that connection. The connection will also receive messages which are not replies to commands.
|
65
|
+
|
66
|
+
The regular `EM::Hiredis::Client` no longer understands pubsub messages - this logic has been moved to `EM::Hiredis::PubsubClient`. The pubsub client can either be initialized directly (see code) or you can get one connected to the same redis server by calling `#pubsub` on an existing `EM::Hiredis::Client` instance.
|
67
|
+
|
68
|
+
Pubsub can either be used in em-hiredis in a close-to-the-metal fashion, or you can use the convenience functionality for binding blocks to subscriptions if you prefer (recommended).
|
69
|
+
|
70
|
+
### Close to the metal
|
71
|
+
|
72
|
+
Basically just bind to `:message` and `:pmessage` events:
|
73
|
+
|
74
|
+
# Create two connections, one will be used for subscribing
|
48
75
|
redis = EM::Hiredis.connect
|
49
|
-
|
76
|
+
pubsub = redis.pubsub
|
50
77
|
|
51
|
-
|
52
|
-
|
78
|
+
pubsub.subscribe('bar.0').callback { puts "Subscribed" }
|
79
|
+
pubsub.psubscribe('bar.*')
|
53
80
|
|
54
|
-
|
81
|
+
pubsub.on(:message) { |channel, message|
|
55
82
|
p [:message, channel, message]
|
56
83
|
}
|
57
84
|
|
58
|
-
|
85
|
+
pubsub.on(:pmessage) { |key, channel, message|
|
59
86
|
p [:pmessage, key, channel, message]
|
60
87
|
}
|
61
88
|
|
@@ -65,17 +92,43 @@ This example should explain things. Once a redis connection is in a pubsub state
|
|
65
92
|
}
|
66
93
|
}
|
67
94
|
|
68
|
-
|
69
|
-
|
95
|
+
### Richer interface to pubsub
|
96
|
+
|
97
|
+
If you pass a block to `subscribe` or `psubscribe`, the passed block will be called whenever a message arrives on that subscription:
|
98
|
+
|
99
|
+
redis = EM::Hiredis.connect
|
100
|
+
|
101
|
+
puts "Subscribing"
|
102
|
+
redis.pubsub.subscribe("foo") { |msg|
|
103
|
+
p [:sub1, msg]
|
104
|
+
}
|
105
|
+
|
106
|
+
redis.pubsub.psubscribe("f*") { |msg|
|
107
|
+
p [:sub2, msg]
|
108
|
+
}
|
109
|
+
|
110
|
+
EM.add_periodic_timer(1) {
|
111
|
+
redis.publish("foo", "Hello")
|
112
|
+
}
|
113
|
+
|
114
|
+
EM.add_timer(5) {
|
115
|
+
puts "Unsubscribing sub1"
|
116
|
+
redis.pubsub.unsubscribe("foo")
|
117
|
+
}
|
118
|
+
|
119
|
+
It's possible to subscribe to the same channel multiple time and just unsubscribe a single callback using `unsubscribe_proc` or `punsubscribe_proc`.
|
120
|
+
|
121
|
+
## Developing
|
70
122
|
|
71
123
|
Hacking on em-hiredis is pretty simple, make sure you have Bundler installed:
|
72
124
|
|
73
125
|
gem install bundler
|
74
126
|
bundle
|
75
127
|
|
76
|
-
|
128
|
+
In order to run the tests you need to have a local redis server running on port 6379. Run all the tests:
|
77
129
|
|
78
|
-
|
130
|
+
# WARNING: The tests call flushdb on db 9 - this clears all keys!
|
131
|
+
bundle exec rake
|
79
132
|
|
80
133
|
To run an individual test:
|
81
134
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
require 'em-hiredis'
|
3
|
+
|
4
|
+
EM.run {
|
5
|
+
redis = EM::Hiredis.connect
|
6
|
+
|
7
|
+
redis.sadd('aset', 'member').callback {
|
8
|
+
response_deferrable = redis.hget('aset', 'member')
|
9
|
+
response_deferrable.errback { |e|
|
10
|
+
p e # => #<RuntimeError: ERR Operation against a key holding the wrong kind of value>
|
11
|
+
p e.redis_error
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
require 'em-hiredis'
|
3
|
+
|
4
|
+
EM.run {
|
5
|
+
redis = EM::Hiredis.connect
|
6
|
+
|
7
|
+
puts "Subscribing"
|
8
|
+
redis.pubsub.subscribe("foo") { |msg|
|
9
|
+
p [:sub1, msg]
|
10
|
+
}
|
11
|
+
|
12
|
+
redis.pubsub.psubscribe("f*") { |msg|
|
13
|
+
p [:sub2, msg]
|
14
|
+
}
|
15
|
+
|
16
|
+
EM.add_periodic_timer(1) {
|
17
|
+
redis.publish("foo", "Hello")
|
18
|
+
}
|
19
|
+
|
20
|
+
EM.add_timer(5) {
|
21
|
+
puts "Unsubscribing sub1"
|
22
|
+
redis.pubsub.unsubscribe("foo")
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
require 'em-hiredis'
|
3
|
+
|
4
|
+
EM.run {
|
5
|
+
redis = EM::Hiredis.connect
|
6
|
+
|
7
|
+
# If you pass a block to subscribe it will be called whenever a message
|
8
|
+
# is received on this channel
|
9
|
+
redis.pubsub.subscribe('foo') { |message|
|
10
|
+
puts "Block received #{message}"
|
11
|
+
}
|
12
|
+
|
13
|
+
# You can also pass any other object which responds to call if you wish
|
14
|
+
callback = Proc.new { |message|
|
15
|
+
"Proc received #{message}"
|
16
|
+
}
|
17
|
+
df = redis.pubsub.subscribe('foo', callback)
|
18
|
+
|
19
|
+
# All calls return a deferrable
|
20
|
+
df.callback { |reply|
|
21
|
+
p [:subscription_succeeded, reply]
|
22
|
+
}
|
23
|
+
|
24
|
+
# Passing such an object is useful if you want to unsubscribe
|
25
|
+
redis.pubsub.unsubscribe_proc('foo', callback)
|
26
|
+
|
27
|
+
# Or if you want to call a method on a certain object
|
28
|
+
class Thing
|
29
|
+
def receive_message(message)
|
30
|
+
puts "Thing received #{message}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
redis.pubsub.subscribe('bar', Thing.new.method(:receive_message))
|
34
|
+
|
35
|
+
# You can also get all the following raw events:
|
36
|
+
# message pmessage subscribe unsubscribe psubscribe punsubscribe
|
37
|
+
redis.pubsub.on(:message) { |channel, message|
|
38
|
+
p [:message_received, channel, message]
|
39
|
+
}
|
40
|
+
redis.pubsub.on(:unsubscribe) { |channel, remaining_subscriptions|
|
41
|
+
p [:unsubscribe_happened, channel, remaining_subscriptions]
|
42
|
+
}
|
43
|
+
|
44
|
+
EM.add_timer(1) {
|
45
|
+
# You can also unsubscribe completely from a channel
|
46
|
+
redis.pubsub.unsubscribe('foo')
|
47
|
+
|
48
|
+
# Publishing events
|
49
|
+
redis.publish('bar', 'Hello')
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
require 'em-hiredis'
|
3
|
+
|
4
|
+
EM.run {
|
5
|
+
# Create two connections, one will be used for subscribing
|
6
|
+
redis = EM::Hiredis.connect
|
7
|
+
pubsub = redis.pubsub
|
8
|
+
|
9
|
+
pubsub.subscribe('bar.0').callback { puts "Subscribed" }
|
10
|
+
pubsub.psubscribe('bar.*')
|
11
|
+
|
12
|
+
pubsub.on(:message) { |channel, message|
|
13
|
+
p [:message, channel, message]
|
14
|
+
}
|
15
|
+
|
16
|
+
pubsub.on(:pmessage) { |key, channel, message|
|
17
|
+
p [:pmessage, key, channel, message]
|
18
|
+
}
|
19
|
+
|
20
|
+
EM.add_periodic_timer(1) {
|
21
|
+
redis.publish("bar.#{rand(2)}", "hello").errback { |e|
|
22
|
+
p [:publisherror, e]
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
data/lib/em-hiredis.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
1
|
require 'eventmachine'
|
2
|
-
require 'uri'
|
3
2
|
|
4
3
|
module EventMachine
|
5
4
|
module Hiredis
|
5
|
+
# All em-hiredis errors should descend from EM::Hiredis::Error
|
6
|
+
class Error < RuntimeError; end
|
7
|
+
|
8
|
+
# In the case of error responses from Redis, the RuntimeError returned
|
9
|
+
# by ::Hiredis will be wrapped
|
10
|
+
class RedisError < Error
|
11
|
+
attr_accessor :redis_error
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :reconnect_timeout
|
16
|
+
end
|
17
|
+
self.reconnect_timeout = 0.5
|
18
|
+
|
6
19
|
def self.setup(uri = nil)
|
7
|
-
|
8
|
-
Client.new
|
20
|
+
uri = uri || ENV["REDIS_URL"] || "redis://127.0.0.1:6379/0"
|
21
|
+
client = Client.new
|
22
|
+
client.configure(uri)
|
23
|
+
client
|
9
24
|
end
|
10
25
|
|
11
26
|
def self.connect(uri = nil)
|
@@ -26,9 +41,14 @@ module EventMachine
|
|
26
41
|
log
|
27
42
|
end
|
28
43
|
end
|
44
|
+
|
45
|
+
autoload :Lock, 'em-hiredis/lock'
|
46
|
+
autoload :PersistentLock, 'em-hiredis/persistent_lock'
|
29
47
|
end
|
30
48
|
end
|
31
49
|
|
32
50
|
require 'em-hiredis/event_emitter'
|
33
51
|
require 'em-hiredis/connection'
|
52
|
+
require 'em-hiredis/base_client'
|
34
53
|
require 'em-hiredis/client'
|
54
|
+
require 'em-hiredis/pubsub_client'
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module EventMachine::Hiredis
|
4
|
+
# Emits the following events
|
5
|
+
#
|
6
|
+
# * :connected - on successful connection or reconnection
|
7
|
+
# * :reconnected - on successful reconnection
|
8
|
+
# * :disconnected - no longer connected, when previously in connected state
|
9
|
+
# * :reconnect_failed(failure_number) - a reconnect attempt failed
|
10
|
+
# This event is passed number of failures so far (1,2,3...)
|
11
|
+
# * :monitor
|
12
|
+
#
|
13
|
+
class BaseClient
|
14
|
+
include EventEmitter
|
15
|
+
include EM::Deferrable
|
16
|
+
|
17
|
+
attr_reader :host, :port, :password, :db
|
18
|
+
|
19
|
+
def initialize(host='localhost', port='6379', password=nil, db=nil)
|
20
|
+
@host, @port, @password, @db = host, port, password, db
|
21
|
+
@defs = []
|
22
|
+
@command_queue = []
|
23
|
+
|
24
|
+
@closing_connection = false
|
25
|
+
@reconnect_failed_count = 0
|
26
|
+
@reconnect_timer = nil
|
27
|
+
@failed = false
|
28
|
+
|
29
|
+
self.on(:failed) {
|
30
|
+
@failed = true
|
31
|
+
@command_queue.each do |df, _, _|
|
32
|
+
df.fail(Error.new("Redis connection in failed state"))
|
33
|
+
end
|
34
|
+
@command_queue = []
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Configure the redis connection to use
|
39
|
+
#
|
40
|
+
# In usual operation, the uri should be passed to initialize. This method
|
41
|
+
# is useful for example when failing over to a slave connection at runtime
|
42
|
+
#
|
43
|
+
def configure(uri_string)
|
44
|
+
uri = URI(uri_string)
|
45
|
+
@host = uri.host
|
46
|
+
@port = uri.port
|
47
|
+
@password = uri.password
|
48
|
+
path = uri.path[1..-1]
|
49
|
+
@db = path.to_i # Empty path => 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def connect
|
53
|
+
@connection = EM.connect(@host, @port, Connection, @host, @port)
|
54
|
+
|
55
|
+
@connection.on(:closed) do
|
56
|
+
if @connected
|
57
|
+
@defs.each { |d| d.fail(Error.new("Redis disconnected")) }
|
58
|
+
@defs = []
|
59
|
+
@deferred_status = nil
|
60
|
+
@connected = false
|
61
|
+
unless @closing_connection
|
62
|
+
# Next tick avoids reconnecting after for example EM.stop
|
63
|
+
EM.next_tick { reconnect }
|
64
|
+
end
|
65
|
+
emit(:disconnected)
|
66
|
+
EM::Hiredis.logger.info("#{@connection} Disconnected")
|
67
|
+
else
|
68
|
+
unless @closing_connection
|
69
|
+
@reconnect_failed_count += 1
|
70
|
+
@reconnect_timer = EM.add_timer(EM::Hiredis.reconnect_timeout) {
|
71
|
+
@reconnect_timer = nil
|
72
|
+
reconnect
|
73
|
+
}
|
74
|
+
emit(:reconnect_failed, @reconnect_failed_count)
|
75
|
+
EM::Hiredis.logger.info("#{@connection} Reconnect failed")
|
76
|
+
|
77
|
+
if @reconnect_failed_count >= 4
|
78
|
+
emit(:failed)
|
79
|
+
self.fail(Error.new("Could not connect after 4 attempts"))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@connection.on(:connected) do
|
86
|
+
@connected = true
|
87
|
+
@reconnect_failed_count = 0
|
88
|
+
@failed = false
|
89
|
+
|
90
|
+
select(@db) unless @db == 0
|
91
|
+
auth(@password) if @password
|
92
|
+
|
93
|
+
@command_queue.each do |df, command, args|
|
94
|
+
@connection.send_command(command, args)
|
95
|
+
@defs.push(df)
|
96
|
+
end
|
97
|
+
@command_queue = []
|
98
|
+
|
99
|
+
emit(:connected)
|
100
|
+
EM::Hiredis.logger.info("#{@connection} Connected")
|
101
|
+
succeed
|
102
|
+
|
103
|
+
if @reconnecting
|
104
|
+
@reconnecting = false
|
105
|
+
emit(:reconnected)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
@connection.on(:message) do |reply|
|
110
|
+
if RuntimeError === reply
|
111
|
+
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
112
|
+
deferred = @defs.shift
|
113
|
+
error = RedisError.new("Error reply from redis (wrapped in redis_error)")
|
114
|
+
error.redis_error = reply
|
115
|
+
deferred.fail(error) if deferred
|
116
|
+
else
|
117
|
+
handle_reply(reply)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
@connected = false
|
122
|
+
@reconnecting = false
|
123
|
+
|
124
|
+
return self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Indicates that commands have been sent to redis but a reply has not yet
|
128
|
+
# been received
|
129
|
+
#
|
130
|
+
# This can be useful for example to avoid stopping the
|
131
|
+
# eventmachine reactor while there are outstanding commands
|
132
|
+
#
|
133
|
+
def pending_commands?
|
134
|
+
@connected && @defs.size > 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def connected?
|
138
|
+
@connected
|
139
|
+
end
|
140
|
+
|
141
|
+
def select(db, &blk)
|
142
|
+
@db = db
|
143
|
+
method_missing(:select, db, &blk)
|
144
|
+
end
|
145
|
+
|
146
|
+
def auth(password, &blk)
|
147
|
+
@password = password
|
148
|
+
method_missing(:auth, password, &blk)
|
149
|
+
end
|
150
|
+
|
151
|
+
def close_connection
|
152
|
+
EM.cancel_timer(@reconnect_timer) if @reconnect_timer
|
153
|
+
@closing_connection = true
|
154
|
+
@connection.close_connection_after_writing
|
155
|
+
end
|
156
|
+
|
157
|
+
def reconnect_connection
|
158
|
+
EM.cancel_timer(@reconnect_timer) if @reconnect_timer
|
159
|
+
reconnect
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def method_missing(sym, *args)
|
165
|
+
deferred = EM::DefaultDeferrable.new
|
166
|
+
# Shortcut for defining the callback case with just a block
|
167
|
+
deferred.callback { |result| yield(result) } if block_given?
|
168
|
+
|
169
|
+
if @connected
|
170
|
+
@connection.send_command(sym, args)
|
171
|
+
@defs.push(deferred)
|
172
|
+
elsif @failed
|
173
|
+
deferred.fail(Error.new("Redis connection in failed state"))
|
174
|
+
else
|
175
|
+
@command_queue << [deferred, sym, args]
|
176
|
+
end
|
177
|
+
|
178
|
+
deferred
|
179
|
+
end
|
180
|
+
|
181
|
+
def reconnect
|
182
|
+
@reconnecting = true
|
183
|
+
@connection.reconnect @host, @port
|
184
|
+
EM::Hiredis.logger.info("#{@connection} Reconnecting")
|
185
|
+
end
|
186
|
+
|
187
|
+
def handle_reply(reply)
|
188
|
+
if @defs.empty?
|
189
|
+
if @monitoring
|
190
|
+
emit(:monitor, reply)
|
191
|
+
else
|
192
|
+
raise "Replies out of sync: #{reply.inspect}"
|
193
|
+
end
|
194
|
+
else
|
195
|
+
deferred = @defs.shift
|
196
|
+
deferred.succeed(reply) if deferred
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|