redpack 1.0.3 → 1.0.4
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/rblib/redpack/client.rb +107 -107
- data/rblib/redpack/future.rb +67 -67
- data/rblib/redpack/transport.rb +81 -82
- metadata +3 -3
data/rblib/redpack/client.rb
CHANGED
@@ -19,126 +19,126 @@
|
|
19
19
|
module RedPack
|
20
20
|
|
21
21
|
class Client
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
def initialize(name, redis_options = {:host => 'localhost'})
|
23
|
+
@timeout = 10
|
24
|
+
@seqid = 0
|
25
|
+
@reqtable = {}
|
26
|
+
@transport = RedisClientTransport.new(self, redis_options, name)
|
27
|
+
end
|
28
|
+
attr_accessor :timeout
|
29
|
+
|
30
|
+
# call-seq:
|
31
|
+
# notify(symbol, *args) -> nil
|
32
|
+
#
|
33
|
+
# Calls remote method with NOTIFY protocol.
|
34
|
+
# It doesn't require server to return results.
|
35
|
+
# This method is non-blocking and returns nil.
|
36
|
+
def notify(method, *args)
|
37
|
+
send_notify(method, args)
|
38
|
+
nil
|
39
|
+
end
|
29
40
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# client.increment_remote_counter_async(1, 2, 3) do |error, result|
|
46
|
-
# if error
|
47
|
-
# raise "we got an error"
|
48
|
-
# else
|
49
|
-
# counter = result
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
def method_missing(method, *args, &block)
|
53
|
-
method_name = method.to_s
|
54
|
-
sync = method_name.end_with?("_sync")
|
55
|
-
async = method_name.end_with?("_async")
|
41
|
+
# call code like this for sync calls:
|
42
|
+
# counter = client.increment_remote_counter_sync(1, 2, 3)
|
43
|
+
#
|
44
|
+
# call code like this for async non-blocking calls:
|
45
|
+
# client.increment_remote_counter_async(1, 2, 3) do |error, result|
|
46
|
+
# if error
|
47
|
+
# raise "we got an error"
|
48
|
+
# else
|
49
|
+
# counter = result
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
def method_missing(method, *args, &block)
|
53
|
+
method_name = method.to_s
|
54
|
+
sync = method_name.end_with?("_sync")
|
55
|
+
async = method_name.end_with?("_async")
|
56
56
|
if sync || async
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
57
|
+
method_name.gsub!(/_sync$/, '') if sync
|
58
|
+
method_name.gsub!(/_async$/, '') if async
|
59
|
+
future = send_request(method_name, args, !!sync)
|
60
|
+
future.attach_callback(block) if block
|
61
|
+
if sync
|
62
|
+
@transport.listen_for_return_sync
|
63
|
+
future.get
|
64
|
+
if future.error
|
65
|
+
raise error
|
66
|
+
else
|
67
|
+
return future.result
|
68
|
+
end
|
69
|
+
elsif async
|
70
|
+
@transport.listen_for_return_async
|
71
|
+
return future
|
72
72
|
end
|
73
73
|
else
|
74
74
|
super
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
78
77
|
|
79
|
-
# Closes underlaying Transport and destroy resources.
|
80
|
-
def close
|
81
|
-
@timer.detach if @timer.attached?
|
82
|
-
@reqtable = {}
|
83
|
-
@transport.close
|
84
|
-
@seqid = 0
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
# from ClientTransport
|
89
|
-
def on_response(sock, msgid, error, result) #:nodoc:
|
90
|
-
if future = @reqtable.delete(msgid)
|
91
|
-
future.set_result(error, result)
|
92
|
-
end
|
93
|
-
end
|
94
78
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
}
|
104
|
-
nil
|
105
|
-
end
|
79
|
+
# Closes underlaying Transport and destroy resources.
|
80
|
+
def close
|
81
|
+
@timer.detach if @timer.attached?
|
82
|
+
@reqtable = {}
|
83
|
+
@transport.close
|
84
|
+
@seqid = 0
|
85
|
+
self
|
86
|
+
end
|
106
87
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
true
|
114
|
-
end
|
115
|
-
}
|
116
|
-
timedout.each {|future|
|
117
|
-
begin
|
118
|
-
future.set_result TimeoutError.new, nil
|
119
|
-
rescue
|
120
|
-
end
|
121
|
-
}
|
122
|
-
!@reqtable.empty?
|
123
|
-
end
|
88
|
+
# from ClientTransport
|
89
|
+
def on_response(sock, msgid, error, result) #:nodoc:
|
90
|
+
if future = @reqtable.delete(msgid)
|
91
|
+
future.set_result(error, result)
|
92
|
+
end
|
93
|
+
end
|
124
94
|
|
95
|
+
# from ClientTransport
|
96
|
+
def on_connect_failed #:nodoc:
|
97
|
+
@reqtable.reject! {|msgid, future|
|
98
|
+
begin
|
99
|
+
future.set_result ConnectError.new, nil
|
100
|
+
rescue
|
101
|
+
end
|
102
|
+
true
|
103
|
+
}
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# from Client, SessionPool
|
108
|
+
def step_timeout #:nodoc:
|
109
|
+
timedout = []
|
110
|
+
@reqtable.reject! {|msgid, future|
|
111
|
+
if future.step_timeout
|
112
|
+
timedout.push(future)
|
113
|
+
true
|
114
|
+
end
|
115
|
+
}
|
116
|
+
timedout.each {|future|
|
117
|
+
begin
|
118
|
+
future.set_result TimeoutError.new, nil
|
119
|
+
rescue
|
120
|
+
end
|
121
|
+
}
|
122
|
+
!@reqtable.empty?
|
123
|
+
end
|
125
124
|
|
126
|
-
private
|
127
|
-
def send_request(method, param, sync = false)
|
128
|
-
method = method.to_s
|
129
|
-
msgid = @seqid
|
130
|
-
@seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
|
131
|
-
data = [REQUEST, msgid, method, param]
|
132
|
-
@transport.send_data(data, msgid, sync)
|
133
|
-
@reqtable[msgid] = Future.new(self)
|
134
|
-
end
|
135
125
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
126
|
+
private
|
127
|
+
def send_request(method, param, sync = false)
|
128
|
+
method = method.to_s
|
129
|
+
msgid = @seqid
|
130
|
+
@seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
|
131
|
+
data = [REQUEST, msgid, method, param]
|
132
|
+
@transport.send_data(data, msgid, sync)
|
133
|
+
@reqtable[msgid] = Future.new(self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def send_notify(method, param)
|
137
|
+
method = method.to_s
|
138
|
+
data = [NOTIFY, method, param]
|
139
|
+
@transport.send_data(data, nil)
|
140
|
+
nil
|
141
|
+
end
|
142
142
|
end
|
143
143
|
|
144
144
|
end
|
data/rblib/redpack/future.rb
CHANGED
@@ -23,79 +23,79 @@ module RedPack
|
|
23
23
|
# because it is not yet received.
|
24
24
|
# You can wait and get the result with get method.
|
25
25
|
class Future
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
def initialize(session, callback = nil) #:nodoc:
|
27
|
+
@timeout = session.timeout
|
28
|
+
@callback_handler = callback
|
29
|
+
@error_handler = nil
|
30
|
+
@result_handler = nil
|
31
|
+
@set = false
|
32
|
+
@error = nil
|
33
|
+
@result = nil
|
34
|
+
end
|
35
|
+
attr_accessor :result, :error
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
37
|
+
# Wait for receiving result of remote procedure call and returns its result.
|
38
|
+
# If the remote method raises error, then this method raises RemoteError.
|
39
|
+
# If the remote procedure call failed with timeout, this method raises TimeoutError.
|
40
|
+
# Otherwise this method returns the result of remote method.
|
41
|
+
def get
|
42
|
+
join
|
43
|
+
if error.nil?
|
44
|
+
if @result_handler
|
45
|
+
return @result_handler.call(@result)
|
46
|
+
else
|
47
|
+
return @result
|
48
|
+
end
|
49
|
+
end
|
50
|
+
if @error_handler
|
51
|
+
return @error_handler.call(self)
|
52
|
+
else
|
53
|
+
raise @error if @error.is_a?(Error)
|
54
|
+
raise RemoteError.new(@error, @result)
|
55
|
+
end
|
56
|
+
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
58
|
+
# Wait for receiving result of remote procedure call.
|
59
|
+
# This method returns self.
|
60
|
+
# If a callback method is attached, it will be called.
|
61
|
+
def join
|
62
|
+
until @set
|
63
|
+
sleep(0.0001)
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
68
|
+
# call-seq:
|
69
|
+
# attach_callback {|future| }
|
70
|
+
#
|
71
|
+
# Attaches a callback method that is called when the result of remote method is received.
|
72
|
+
def attach_callback(proc = nil, &block)
|
73
|
+
@callback_handler = proc || block
|
74
|
+
end
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
# For IDL
|
77
|
+
def attach_error_handler(proc = nil, &block) #:nodoc:
|
78
|
+
@error_handler = proc || block
|
79
|
+
end
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
# For IDL
|
82
|
+
def attach_result_handler(proc = nil, &block) #:nodoc:
|
83
|
+
@result_handler = proc || block
|
84
|
+
end
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
86
|
+
def set_result(err, res) #:nodoc:
|
87
|
+
@error = err
|
88
|
+
@result = res
|
89
|
+
if @callback_handler
|
90
|
+
if @callback_handler.arity == 2
|
91
|
+
# FIXME backward compatibility
|
92
|
+
@callback_handler.call(error, result)
|
93
|
+
else
|
94
|
+
@callback_handler.call(self)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@set = true
|
98
|
+
end
|
99
99
|
end
|
100
100
|
|
101
101
|
end
|
data/rblib/redpack/transport.rb
CHANGED
@@ -37,9 +37,9 @@ class RedisClientTransport
|
|
37
37
|
# Pass in {:ignore_return_value => true} as part of the redis_options so that the server doesn't put the return
|
38
38
|
# object in a queue. This is useful if you're doing call_async and never care about the return value. It saves
|
39
39
|
# Redis from adding a key/list pair. A synchronous call will result in an immediate timeout exception.
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def initialize(client, redis_options, name)
|
41
|
+
@client = client
|
42
|
+
@queue_name = "redpack_request_queue:#{name}"
|
43
43
|
@ignore_return_value = !!redis_options.delete(:ignore_return_value)
|
44
44
|
if @ignore_return_value
|
45
45
|
@client.timeout = 0
|
@@ -50,12 +50,12 @@ class RedisClientTransport
|
|
50
50
|
@redis_options = redis_options
|
51
51
|
@redis = Redis.new(redis_options)
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
# assign a unique client key for this instance which will be used for return values
|
54
|
+
@identifier = @redis.incr("redpack_response_queue_index")
|
55
|
+
@return_queue_name = "redpack_response_queue:#{@identifier}"
|
56
|
+
@unprocessed_requests_name = "#{@return_queue_name}:unprocessed"
|
57
|
+
@pool = 0
|
58
|
+
end
|
59
59
|
|
60
60
|
def process_data(data)
|
61
61
|
# puts "done waiting for #{@return_queue_name}"
|
@@ -77,26 +77,25 @@ class RedisClientTransport
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
|
80
|
+
def listen_for_return_sync
|
81
81
|
process_data(@redis.blpop(@return_queue_name, 0))
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
|
84
|
+
def listen_for_return_async
|
85
|
+
Thread.new do
|
86
86
|
unless @ignore_return_value
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
87
|
+
EM.run do
|
88
|
+
redis = EventMachine::Protocols::Redis.connect(@redis_options)
|
89
|
+
redis.blpop(@return_queue_name, 0) do |data|
|
90
|
+
process_data(data)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
95
94
|
end
|
96
95
|
end
|
97
96
|
|
98
|
-
|
99
|
-
|
97
|
+
def redis_push(msgpack_data, msgid = nil)
|
98
|
+
if msgid
|
100
99
|
@redis.multi
|
101
100
|
# puts "setting key in #{@unprocessed_requests_name}"
|
102
101
|
@redis.hset(@unprocessed_requests_name, msgid.to_s, msgpack_data)
|
@@ -108,89 +107,89 @@ class RedisClientTransport
|
|
108
107
|
end
|
109
108
|
end
|
110
109
|
|
111
|
-
|
112
|
-
if
|
110
|
+
def send_data(data, msgid = nil, sync = false)
|
111
|
+
if @ignore_return_value
|
112
|
+
redis_push(BSON.serialize({:data => data}), msgid)
|
113
|
+
else
|
113
114
|
redis_push(BSON.serialize({:data => data, :return => @return_queue_name}), msgid)
|
114
|
-
elsif @ignore_return_value || !sync
|
115
|
-
redis_push(BSON.serialize({:data => data}), msgid)
|
116
115
|
end
|
117
|
-
|
116
|
+
end
|
118
117
|
|
119
|
-
|
120
|
-
|
121
|
-
|
118
|
+
def close
|
119
|
+
self
|
120
|
+
end
|
122
121
|
|
123
|
-
|
124
|
-
|
122
|
+
def on_connect(sock)
|
123
|
+
end
|
125
124
|
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
def on_response(msgid, error, result)
|
126
|
+
@client.on_response(self, msgid, error, result)
|
127
|
+
end
|
129
128
|
|
130
|
-
|
131
|
-
|
129
|
+
def on_connect_failed(sock)
|
130
|
+
end
|
132
131
|
|
133
|
-
|
134
|
-
|
132
|
+
def on_close(sock)
|
133
|
+
end
|
135
134
|
end
|
136
135
|
|
137
136
|
class RedisServerTransport
|
138
137
|
include Util
|
139
|
-
|
140
|
-
|
141
|
-
|
138
|
+
|
139
|
+
def initialize(name, redis_options = {})
|
140
|
+
@ignore_nil_returns = !!redis_options.delete(:ignore_nil_returns)
|
142
141
|
@redis = Redis.new(redis_options)
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
@queue_name = "redpack_request_queue:#{name}"
|
143
|
+
@timeout_mechanism = redis_options[:timeout_mechanism]
|
144
|
+
end
|
146
145
|
|
147
|
-
|
148
|
-
|
149
|
-
|
146
|
+
# ServerTransport interface
|
147
|
+
def listen(server)
|
148
|
+
@server = server
|
150
149
|
loop do
|
151
150
|
begin
|
152
151
|
# puts "listening to #{@queue_name}"
|
153
152
|
data = @redis.blpop(@queue_name, 0)
|
154
153
|
# puts "popped item off of #{@queue_name}"
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
154
|
+
if data && data[1]
|
155
|
+
redis_packet = BSON.deserialize(data[1])
|
156
|
+
if redis_packet["data"]
|
157
|
+
@return_queue_name = redis_packet["return"]
|
158
|
+
@unprocessed_requests_name = "#{@return_queue_name}:unprocessed"
|
159
|
+
msg = redis_packet["data"]
|
160
|
+
case msg[0]
|
161
|
+
when REQUEST
|
162
|
+
@server.on_request(self, msg[1], msg[2], msg[3])
|
163
|
+
when RESPONSE
|
164
|
+
puts "response message on server session"
|
165
|
+
when NOTIFY
|
166
|
+
@server.on_notify(msg[1], msg[2])
|
167
|
+
else
|
168
|
+
puts "unknown message type #{msg[0]}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
rescue => e
|
173
|
+
# probably timed out
|
174
|
+
p e
|
175
|
+
@timeout_mechanism.call if @timeout_mechanism
|
176
|
+
end
|
178
177
|
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def finish(msgid)
|
182
|
-
# puts "removing key from #{@unprocessed_requests_name}"
|
183
|
-
@redis.hdel(@unprocessed_requests_name, msgid.to_s)
|
184
178
|
end
|
185
179
|
|
186
|
-
|
187
|
-
|
180
|
+
def finish(msgid)
|
181
|
+
# puts "removing key from #{@unprocessed_requests_name}"
|
182
|
+
@redis.hdel(@unprocessed_requests_name, msgid.to_s)
|
183
|
+
end
|
184
|
+
|
185
|
+
def send_data(data)
|
186
|
+
# puts "putting data on #{@return_queue_name}"
|
188
187
|
@redis.rpush(@return_queue_name, BSON.serialize({:data => data})) if @return_queue_name
|
189
188
|
end
|
190
189
|
|
191
|
-
|
192
|
-
|
193
|
-
|
190
|
+
# ServerTransport interface
|
191
|
+
def close
|
192
|
+
end
|
194
193
|
end
|
195
194
|
|
196
|
-
end
|
195
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 4
|
9
|
+
version: 1.0.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Dean Mao
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01
|
17
|
+
date: 2011-02-01 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|